/********************************************************************************* * File : clk_cfg_eeprom_writer.c * Authors: Patrick Murphy (murphpo [at] mangocomm.com) * License: Copyright 2015, Mango Communications. All rights reserved. * Distributed under the WARP license (http://warpproject.org/license) * * This is an example application for writing clock configuration values to the * EEPROM on the WARP v3 board. The w3_clock_controller_axi core checks the EEPROM * for clock configurations immediately after the FPGA is configured. If the EEPROM * has valid clock configuration values the core uses them in place of its internal * defaults. * * See the clock controller documentation for more information * http://warpproject.org/trac/wiki/cores/w3_clock_controller * * This application will compile in any SDK workspace whose hardware project * includes the w3_iic_eeprom_axi core, including the 802.11 and WARPLab * Reference Designs. * *********************************************************************************/ #include #include #include #define EEPROM_BASEADDR XPAR_W3_IIC_EEPROM_ONBOARD_BASEADDR #define STAT_SUCCESS 0 #define CLKCFG_DELIM_B0 0xA5 #define CLKCFG_DELIM_B1 0xCD #define CLKCFG_ADDR_BASE 15000 //address of first byte of clk config region in EEPROM #define CLKCFG_ADDR_MAX 15999 //address of last byte of clk config region in EEPROM #define CLKCFG_NUMBYTES_RFREF (8*2) // 8 data/addr pairs per config #define CLKCFG_NUMBYTES_SAMP (8*2) // 8 data/addr pairs per config #define CLKCFG_NUMBYTES_PLL (40*2) // 40 data/addr pairs per config #define CLKCFG_ADDR_RFREF_BASE (CLKCFG_ADDR_BASE + 2) //15002 #define CLKCFG_ADDR_RFREF_NOCM (CLKCFG_ADDR_RFREF_BASE + 0*CLKCFG_NUMBYTES_RFREF) #define CLKCFG_ADDR_RFREF_CMMMCX_A (CLKCFG_ADDR_RFREF_BASE + 1*CLKCFG_NUMBYTES_RFREF) #define CLKCFG_ADDR_RFREF_CMMMCX_B (CLKCFG_ADDR_RFREF_BASE + 2*CLKCFG_NUMBYTES_RFREF) #define CLKCFG_ADDR_RFREF_CMMMCX_C (CLKCFG_ADDR_RFREF_BASE + 3*CLKCFG_NUMBYTES_RFREF) #define CLKCFG_ADDR_RFREF_CMPLL_A (CLKCFG_ADDR_RFREF_BASE + 4*CLKCFG_NUMBYTES_RFREF) #define CLKCFG_ADDR_RFREF_CMPLL_B (CLKCFG_ADDR_RFREF_BASE + 5*CLKCFG_NUMBYTES_RFREF) #define CLKCFG_ADDR_RFREF_CMPLL_C (CLKCFG_ADDR_RFREF_BASE + 6*CLKCFG_NUMBYTES_RFREF) #define CLKCFG_ADDR_SAMP_BASE (CLKCFG_ADDR_RFREF_BASE + 128) //15130 #define CLKCFG_ADDR_SAMP_NOCM (CLKCFG_ADDR_SAMP_BASE + 0*CLKCFG_NUMBYTES_SAMP) #define CLKCFG_ADDR_SAMP_CMMMCX_A (CLKCFG_ADDR_SAMP_BASE + 1*CLKCFG_NUMBYTES_SAMP) #define CLKCFG_ADDR_SAMP_CMMMCX_B (CLKCFG_ADDR_SAMP_BASE + 2*CLKCFG_NUMBYTES_SAMP) #define CLKCFG_ADDR_SAMP_CMMMCX_C (CLKCFG_ADDR_SAMP_BASE + 3*CLKCFG_NUMBYTES_SAMP) #define CLKCFG_ADDR_SAMP_CMPLL_A (CLKCFG_ADDR_SAMP_BASE + 4*CLKCFG_NUMBYTES_SAMP) #define CLKCFG_ADDR_SAMP_CMPLL_B (CLKCFG_ADDR_SAMP_BASE + 5*CLKCFG_NUMBYTES_SAMP) #define CLKCFG_ADDR_SAMP_CMPLL_C (CLKCFG_ADDR_SAMP_BASE + 6*CLKCFG_NUMBYTES_SAMP) #define CLKCFG_ADDR_PLL_BASE (CLKCFG_ADDR_SAMP_BASE + 128) //15258 #define CLKCFG_ADDR_PLL_NOCM (CLKCFG_ADDR_PLL_BASE + 0*CLKCFG_NUMBYTES_PLL) #define CLKCFG_ADDR_PLL_CMPLL_A (CLKCFG_ADDR_PLL_BASE + 1*CLKCFG_NUMBYTES_PLL) #define CLKCFG_ADDR_PLL_CMPLL_B (CLKCFG_ADDR_PLL_BASE + 2*CLKCFG_NUMBYTES_PLL) #define CLKCFG_ADDR_PLL_CMPLL_C (CLKCFG_ADDR_PLL_BASE + 3*CLKCFG_NUMBYTES_PLL) /*************************************************************************************** RF ref clk buffer (AD9512): CLK1: Input from on-board 80MHz oscillator CLK2: Input from clock module OUT0: LVPECL output to clock module OUT1: Unused LVPECL output (not terminated) OUT2: Unused LVPECL output (not terminated) OUT3: LVCMOS outputs to RFA/RFB MAX2829 (p: RFB, n: RFA) OUT4: LVDS output to FMC slot All configs not listed below are handled post-boot by MicroBlaze application No Clock Module: Do nothing - all config handled post-boot by MicroBlaze application CM-MMCX Configs: A/B/C: Do nothing CM-PLL Configs: A: Select CLK1 input (on-board osc), enable OUT0 (output to clock module) as 10MHz full-scale LVPECL B/C: Do nothing Relevant registers: 0x45: Selects clock input: 0x00 for off-board, 0x01 for on-board 0x4A: 0x33 sets divider on OUT0 (output to clk mod) to 8, 50% duty cycle 0x4B: 0x00 enables divider on OUT0 (output to clk mod) 0x3D: 0x08 configures LVPECL output OUT0 (output to clk mod) for max drive 0x5A: Trigger device update from SPI registers (self-clearing) ***************************************************************************************/ u8 cfg_nocm_rfref_addr[] = {0xFF}; u8 cfg_nocm_rfref_data[] = {0xFF}; u8 cfg_cmmmcx_A_rfref_addr[] = {0xFF}; u8 cfg_cmmmcx_A_rfref_data[] = {0xFF}; u8 cfg_cmmmcx_B_rfref_addr[] = {0xFF}; u8 cfg_cmmmcx_B_rfref_data[] = {0xFF}; u8 cfg_cmmmcx_C_rfref_addr[] = {0xFF}; u8 cfg_cmmmcx_C_rfref_data[] = {0xFF}; u8 cfg_cmpll_A_rfref_addr[] = {0x45, 0x4A, 0x4B, 0x3D, 0x5A, 0xFF}; u8 cfg_cmpll_A_rfref_data[] = {0x01, 0x33, 0x00, 0x08, 0x01, 0xFF}; u8 cfg_cmpll_B_rfref_addr[] = {0xFF}; u8 cfg_cmpll_B_rfref_data[] = {0xFF}; u8 cfg_cmpll_C_rfref_addr[] = {0xFF}; u8 cfg_cmpll_C_rfref_data[] = {0xFF}; /*************************************************************************************** Samp clk buffer (AD9512): CLK1: Input from on-board 80MHz oscillator CLK2: Input from clock module OUT0: LVPECL output to RFB AD9963 OUT1: LVPECL output to clock module OUT2: LVPECL output to RFA AD9963 OUT3: LVDS output to FPGA OUT4: LVDS output to FMC slot All configs not listed below are handled post-boot by MicroBlaze application No Clock Module: -Select CLK1 -Enable OUT3 as 80MHz LVDS CM-MMCX Configs: A: Same as No Clock Module B/C: -Select CLK2 -Enable OUT3 as 80MHz LVDS CM-PLL Configs: A/B/C: -Select CLK2 (input from clock module) -Enable OUT3 (output to FPGA) as 80MHz LVDS Relevant registers: 0x45: Selects clock input: 0x00 for off-board, 0x01 for on-board 0x51: 0x80 bypasses OUT3 divider (output to FPGA) 0x5A: Trigger device update from SPI registers (self-clearing) ***************************************************************************************/ u8 cfg_nocm_samp_addr[] = {0x45, 0x51, 0x5A, 0xFF}; u8 cfg_nocm_samp_data[] = {0x01, 0x80, 0x01, 0xFF}; u8 cfg_cmmmcx_A_samp_addr[] = {0x45, 0x51, 0x5A, 0xFF}; u8 cfg_cmmmcx_A_samp_data[] = {0x01, 0x80, 0x01, 0xFF}; u8 cfg_cmmmcx_B_samp_addr[] = {0x45, 0x51, 0x5A, 0xFF}; u8 cfg_cmmmcx_B_samp_data[] = {0x00, 0x80, 0x01, 0xFF}; u8 cfg_cmmmcx_C_samp_addr[] = {0x45, 0x51, 0x5A, 0xFF}; u8 cfg_cmmmcx_C_samp_data[] = {0x00, 0x80, 0x01, 0xFF}; u8 cfg_cmpll_A_samp_addr[] = {0x45, 0x51, 0x5A, 0xFF}; u8 cfg_cmpll_A_samp_data[] = {0x00, 0x80, 0x01, 0xFF}; u8 cfg_cmpll_B_samp_addr[] = {0x45, 0x51, 0x5A, 0xFF}; u8 cfg_cmpll_B_samp_data[] = {0x00, 0x80, 0x01, 0xFF}; u8 cfg_cmpll_C_samp_addr[] = {0x45, 0x51, 0x5A, 0xFF}; u8 cfg_cmpll_C_samp_data[] = {0x00, 0x80, 0x01, 0xFF}; /*************************************************************************************** CM-PLL Buffer/PLL (AD9511): CLK1: Input from off-board reference, bypasses PLL CLK2: Input from on-board 80MHz VCXO OUT0/1/2: Unused LVPECL outputs (unterminated) OUT3: LVDS output to clock module header RFCLKBUF pins (rf ref clk buf CLK2 input) OUT4: LVDS output to clock module header SAMPCLKBUF pins (samp clk buf CLK2 input) All configs not listed below are handled post-boot by MicroBlaze application No Clock Module: Toggle reset, power down PLL core and all outputs This handles the case of the CM-PLL being mounted but its switches being set to 00 It's better to do SPI writes to open pins (i.e. no CM) than let the PLL run free CM-MMCX Configs: Do nothing - no PLL CM-PLL Configs: Common: -Toggle reset -Disable OUT0/1/2 (unused) -Enable OUT4 as 80MHz max-drive LVDS A: -Configure PLL for 10MHz reference (see below) -Disable OUT3 (output to RF ref clk buf) B: -Configure PLL for 10MHz reference (see below) -Enable OUT3 (output to RF ref clk buf) C: -Configure PLL for 80MHz reference (see below) -Enable OUT3 (output to RF ref clk buf) Relevant registers: 0x00: 30 resets, 10 un-resets 0x04: A counter b[5:0] 0x05: B counter b[12:8] 0x06: B counter b[7:0] 0x0B: R counter b[13:8] 0x0C: R counter b[7:0] 0x07: 0x00 disables loss-of-reference state machine 0x08: 0x47 enables charge pump, sets STATUS=lock_det, sets positive PFD polarity 0x09: 0x70 sets max charge pump current 0x0A: 0x08 for normal operation, 10MHz ref; 0x040 for normal operation, 80MHz ref) b[1:0]: PLL powerdown (00 or 10=normal operation, 01 or 11=powe down) b[4:2]: Prescaler mode (see datasheet for full list) 000: FD, div by 1 010: DM, (P/P+1)=2/3 0x3D/3E/3F: 03 powers down LVPECL outputs OUT0/1/2 0x40/41: 01 disables OUT3/4, 02 enables LVDS output OUT3/4 0x51/53: 80 disables divider (W3 board wants 80MHz inputs from clk module) 0x45: 02 selects CLK2 (VCXO clk src), powers down CLK1 input, powers up all other I/O 0x5A: Trigger device update from SPI registers (self-clearing) Common reference frequency configs: 10MHz: PFD freq: 10MHz R divider: 1 N divider: 8 (implies DM mode, prescaler mode (2/3), A=2, B=3) 80MHz: PFD freq: 80MHz R divider: 1 N divider: 1 (implies FD mode, prescaler mode div 1, B=1 (bypassed)) See AD9511 datasheet Table 16 for PLL settings for other valid divider settings ***************************************************************************************/ u8 cfg_nocm_pll_addr[] = {0x00, 0x00, 0x0A, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0xFF}; u8 cfg_nocm_pll_data[] = {0x30, 0x10, 0x11, 0x03, 0x03, 0x03, 0x01, 0x01, 0xFF}; u8 cfg_cmpll_A_pll_addr[] = {0x00, 0x00, 0x04, 0x05, 0x06, 0x0B, 0x0C, 0x07, 0x08, 0x09, 0x0A, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x51, 0x53, 0x45, 0x5A, 0xFF}; u8 cfg_cmpll_A_pll_data[] = {0x30, 0x10, 0x02, 0x00, 0x03, 0x00, 0x01, 0x00, 0x47, 0x70, 0x08, 0x03, 0x03, 0x03, 0x01, 0x02, 0x80, 0x80, 0x02, 0x01, 0xFF}; u8 cfg_cmpll_B_pll_addr[] = {0x00, 0x00, 0x04, 0x05, 0x06, 0x0B, 0x0C, 0x07, 0x08, 0x09, 0x0A, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x51, 0x53, 0x45, 0x5A, 0xFF}; u8 cfg_cmpll_B_pll_data[] = {0x30, 0x10, 0x02, 0x00, 0x03, 0x00, 0x01, 0x00, 0x47, 0x70, 0x08, 0x03, 0x03, 0x03, 0x02, 0x02, 0x80, 0x80, 0x02, 0x01, 0xFF}; u8 cfg_cmpll_C_pll_addr[] = {0x00, 0x00, 0x04, 0x05, 0x06, 0x0B, 0x0C, 0x07, 0x08, 0x09, 0x0A, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x51, 0x53, 0x45, 0x5A, 0xFF}; u8 cfg_cmpll_C_pll_data[] = {0x30, 0x10, 0x02, 0x00, 0x03, 0x00, 0x01, 0x00, 0x47, 0x70, 0x40, 0x03, 0x03, 0x03, 0x02, 0x02, 0x80, 0x80, 0x02, 0x01, 0xFF}; int main() { int addr, i; int status = 0; //Confirm every array is short enough and that every data/addr array pair have matching lengths if( (sizeof(cfg_nocm_rfref_addr) > CLKCFG_NUMBYTES_RFREF/2) || (sizeof(cfg_nocm_rfref_addr) != sizeof(cfg_nocm_rfref_addr)) || (sizeof(cfg_cmmmcx_A_rfref_addr) > CLKCFG_NUMBYTES_RFREF/2) || (sizeof(cfg_cmmmcx_A_rfref_addr) != sizeof(cfg_cmmmcx_A_rfref_data)) || (sizeof(cfg_cmmmcx_B_rfref_addr) > CLKCFG_NUMBYTES_RFREF/2) || (sizeof(cfg_cmmmcx_B_rfref_addr) != sizeof(cfg_cmmmcx_B_rfref_data)) || (sizeof(cfg_cmmmcx_C_rfref_addr) > CLKCFG_NUMBYTES_RFREF/2) || (sizeof(cfg_cmmmcx_C_rfref_addr) != sizeof(cfg_cmmmcx_C_rfref_data)) || (sizeof(cfg_cmpll_A_rfref_addr) > CLKCFG_NUMBYTES_RFREF/2) || (sizeof(cfg_cmpll_A_rfref_addr) != sizeof(cfg_cmpll_A_rfref_data)) || (sizeof(cfg_cmpll_B_rfref_addr) > CLKCFG_NUMBYTES_RFREF/2) || (sizeof(cfg_cmpll_B_rfref_addr) != sizeof(cfg_cmpll_B_rfref_data)) || (sizeof(cfg_cmpll_C_rfref_addr) > CLKCFG_NUMBYTES_RFREF/2) || (sizeof(cfg_cmpll_C_rfref_addr) != sizeof(cfg_cmpll_C_rfref_data)) ) { status = -1; xil_printf("ERROR: Invalid lengths for some RF ref clk buffer config data/addr arrays. Check max lengths and matching data/addr lengths.\n"); } if( (sizeof(cfg_nocm_samp_addr) > CLKCFG_NUMBYTES_SAMP/2) || (sizeof(cfg_nocm_samp_addr) != sizeof(cfg_nocm_samp_addr)) || (sizeof(cfg_cmmmcx_A_samp_addr) > CLKCFG_NUMBYTES_SAMP/2) || (sizeof(cfg_cmmmcx_A_samp_addr) != sizeof(cfg_cmmmcx_A_samp_data)) || (sizeof(cfg_cmmmcx_B_samp_addr) > CLKCFG_NUMBYTES_SAMP/2) || (sizeof(cfg_cmmmcx_B_samp_addr) != sizeof(cfg_cmmmcx_B_samp_data)) || (sizeof(cfg_cmmmcx_C_samp_addr) > CLKCFG_NUMBYTES_SAMP/2) || (sizeof(cfg_cmmmcx_C_samp_addr) != sizeof(cfg_cmmmcx_C_samp_data)) || (sizeof(cfg_cmpll_A_samp_addr) > CLKCFG_NUMBYTES_SAMP/2) || (sizeof(cfg_cmpll_A_samp_addr) != sizeof(cfg_cmpll_A_samp_data)) || (sizeof(cfg_cmpll_B_samp_addr) > CLKCFG_NUMBYTES_SAMP/2) || (sizeof(cfg_cmpll_B_samp_addr) != sizeof(cfg_cmpll_B_samp_data)) || (sizeof(cfg_cmpll_C_samp_addr) > CLKCFG_NUMBYTES_SAMP/2) || (sizeof(cfg_cmpll_C_samp_addr) != sizeof(cfg_cmpll_C_samp_data)) ) { status = -1; xil_printf("ERROR: Invalid lengths for some samp clk buffer config data/addr arrays. Check max lengths and matching data/addr lengths.\n"); } if( (sizeof(cfg_nocm_pll_addr) > CLKCFG_NUMBYTES_PLL/2) || (sizeof(cfg_nocm_pll_addr) != sizeof(cfg_nocm_pll_addr)) || (sizeof(cfg_cmpll_A_pll_addr) > CLKCFG_NUMBYTES_PLL/2) || (sizeof(cfg_cmpll_A_pll_addr) != sizeof(cfg_cmpll_A_pll_data)) || (sizeof(cfg_cmpll_B_pll_addr) > CLKCFG_NUMBYTES_PLL/2) || (sizeof(cfg_cmpll_B_pll_addr) != sizeof(cfg_cmpll_B_pll_data)) || (sizeof(cfg_cmpll_C_pll_addr) > CLKCFG_NUMBYTES_PLL/2) || (sizeof(cfg_cmpll_C_pll_addr) != sizeof(cfg_cmpll_C_pll_data)) ) { status = -1; xil_printf("ERROR: Invalid lengths for some PLL config data/addr arrays. Check max lengths and matching data/addr lengths.\n"); } if(status != STAT_SUCCESS) {return -1;} xil_printf("\n\n******************************************\n"); xil_printf("Starting Clock Config Data EEPROM Write...\n\n"); //Initialize the EEPROM driver iic_eeprom_init(EEPROM_BASEADDR, 0x64); #if 0 //Initialize the full clk config region to 0xFF // This ensures all address/data values not supplied by the user are left with a safe "don't care" value xil_printf("\tInitializing config data region in EEPROM\n"); for(addr = CLKCFG_ADDR_BASE; addr <= CLKCFG_ADDR_MAX; addr++) { status = iic_eeprom_writeByte(EEPROM_BASEADDR, (u16)addr, 0xFF); if(status != STAT_SUCCESS) {xil_printf("ERROR: Failed to init byte addr %d; exiting\n", addr); return -1;} } #endif //Write the magic delimeter value to the base of the clk config region addr = CLKCFG_ADDR_BASE; status = iic_eeprom_writeByte(EEPROM_BASEADDR, (u16)addr++, (u8)CLKCFG_DELIM_B0); status |= iic_eeprom_writeByte(EEPROM_BASEADDR, (u16)addr++, (u8)CLKCFG_DELIM_B1); if(status != STAT_SUCCESS) {xil_printf("ERROR: Failed to write delim; exiting\n"); return -1;} //Write the RF ref clk buffer addr/data bytes xil_printf("\tWriting RF reference clock buffer config data at addr %d\n", (u16)CLKCFG_ADDR_RFREF_NOCM); status = STAT_SUCCESS; addr = (u16)CLKCFG_ADDR_RFREF_NOCM; for(i=0; i