diff --git a/CMakeLists.txt b/CMakeLists.txt index 89a8d84..4a69997 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,12 +27,11 @@ include(FetchContent) # json-c Library FetchContent_Declare( json_c - GIT_REPOSITORY https://github.com/json-c/json-c + GIT_REPOSITORY https://github.com/json-c/json-c.git GIT_TAG 2372e9518e6ba95b48d37ec162bc7d93b297b52f - FIND_PACKAGE_ARGS NAMES json-c ) - -set(BUILD_SHARED_LIBS OFF) +set(JSON_C_BUILD_APPS OFF) +set(JSON_C_BUILD_SHARED_LIBS OFF) FetchContent_MakeAvailable(json_c) # zlib Library @@ -40,7 +39,6 @@ FetchContent_Declare( zlib GIT_REPOSITORY https://github.com/madler/zlib.git GIT_TAG v1.3.1 - FIND_PACKAGE_ARGS NAMES zlib ) set(ZLIB_BUILD_TESTING OFF) @@ -131,11 +129,16 @@ set_target_properties(tslitex PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/$<0:> ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/$<0:>) -target_link_libraries(tslitex PUBLIC litepcie) -target_link_libraries(tslitex PRIVATE json-c-static zlibstatic) +target_link_libraries(tslitex + PUBLIC + "$" + PRIVATE + json-c-static + zlibstatic +) if(APPLE) - target_link_libraries(tslitex PRIVATE "-framework IOKit") + target_link_libraries(tslitex PRIVATE "$") endif() target_include_directories(tslitex PUBLIC include) @@ -155,10 +158,20 @@ set_target_properties(tslitex_static PROPERTIES set_target_properties(tslitex_static PROPERTIES POSITION_INDEPENDENT_CODE 1) -target_link_libraries(tslitex_static PUBLIC litepcie) -target_link_libraries(tslitex_static PRIVATE json-c-static zlibstatic) +target_link_libraries(tslitex_static + PUBLIC + "$" + PRIVATE + json-c-static + zlibstatic +) + target_include_directories(tslitex_static PUBLIC include) +if(APPLE) + target_link_libraries(tslitex_static PRIVATE "$") +endif() + file(COPY ${TS_LIB_HEADERS} DESTINATION ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/include) #### diff --git a/bindings/python/CMakeLists.txt b/bindings/python/CMakeLists.txt index 0a4959e..6083914 100644 --- a/bindings/python/CMakeLists.txt +++ b/bindings/python/CMakeLists.txt @@ -27,16 +27,19 @@ add_custom_command( ) add_custom_target(pydeps - DEPENDS tslitex/tslitex.c - ${CMAKE_CURRENT_BINARY_DIR}/lib/include/thunderscope.h - tslitex_static + DEPENDS + tslitex/tslitex.c + ${CMAKE_CURRENT_BINARY_DIR}/lib/include/thunderscope.h + tslitex_static ) set(PY_BIND_LIBS tslitex_static) if(WIN32) - list(APPEND PY_BIND_LIBS setupapi) - list(APPEND PY_BIND_LIBS litepcie json-c-static zlibstatic) +list(APPEND PY_BIND_LIBS setupapi) +list(APPEND PY_BIND_LIBS litepcie json-c-static zlibstatic) +else() +list(APPEND PY_BIND_LIBS litepcie json-c z) endif() list(JOIN PY_BIND_LIBS "\", \"" PY_EXT_LIBS) diff --git a/bindings/python/tslitex.pxd b/bindings/python/tslitex.pxd index e8f39cc..8776bd0 100644 --- a/bindings/python/tslitex.pxd +++ b/bindings/python/tslitex.pxd @@ -79,6 +79,12 @@ cdef extern from "thunderscope.h": uint8_t power_state uint8_t pll_state uint8_t afe_state + uint8_t local_osc_clk + uint8_t ref_in_clk + uint8_t pll_lock + uint8_t pll_low + uint8_t pll_high + uint8_t pll_alt sysHealth_t sys_health ctypedef tsScopeState_s tsScopeState_t diff --git a/example/thunderscope_test.cpp b/example/thunderscope_test.cpp index e4f270e..19ea970 100644 --- a/example/thunderscope_test.cpp +++ b/example/thunderscope_test.cpp @@ -27,6 +27,8 @@ #ifdef _WIN32 #include +#else +#include #endif #define OPTPARSE_IMPLEMENTATION @@ -95,10 +97,28 @@ uint32_t _SPI_CONTROL_START = (1 << 0); uint32_t _SPI_CONTROL_LENGTH = (1 << 8); uint32_t _SPI_STATUS_DONE = (1 << 0); +static volatile bool g_program_loop = true; /* Main */ /*------*/ +#ifdef _WIN32 +BOOL WINAPI SigHandler(DWORD ctrlType) +{ + if(ctrlType == CTRL_C_EVENT) + { + g_program_loop = false; + return TRUE; + } + return FALSE; +} +#else +extern "C" void SigHandler(int s) +{ + g_program_loop = false; +} +#endif + void configure_frontend_ldo(file_t fd, uint32_t enable) { uint32_t control_value = litepcie_readl(fd, CSR_FRONTEND_CONTROL_ADDR); control_value &= ~(1 * AFE_CONTROL_LDO_EN); @@ -306,7 +326,7 @@ static void test_io(file_t fd, bool isBeta) } static void test_capture(file_t fd, uint32_t idx, uint8_t channelBitmap, uint16_t bandwidth, - uint32_t volt_scale_uV, int32_t offset_uV, uint8_t ac_couple, uint8_t term, bool watch_bitslip, bool is12bit) + uint32_t volt_scale_uV, int32_t offset_uV, uint8_t ac_couple, uint8_t term, bool watch_bitslip, bool is12bit, bool inRefClk, bool outRefClk, uint32_t refclkFreq) { uint8_t numChan = 0; tsHandle_t tsHdl = thunderscopeOpen(idx, false); @@ -317,6 +337,17 @@ static void test_capture(file_t fd, uint32_t idx, uint8_t channelBitmap, uint16_ uint64_t sampleLen = 0; uint32_t sampleRate = 1000000000; + if(inRefClk) + { + printf("Setting Ref In Clock @ %u Hz\n", refclkFreq); + printf("\t Result: %i\n", thunderscopeRefClockSet(tsHdl, TS_REFCLK_IN, refclkFreq)); + } + else if(outRefClk) + { + printf("Setting Ref Out Clock @ %u Hz\n", refclkFreq); + printf("\t Result: %i\n", thunderscopeRefClockSet(tsHdl, TS_REFCLK_OUT, refclkFreq)); + } + //Setup and Enable Channels tsChannelParam_t chConfig = {0}; uint8_t channel = 0; @@ -378,7 +409,7 @@ static void test_capture(file_t fd, uint32_t idx, uint8_t channelBitmap, uint16_ auto startTime = std::chrono::steady_clock::now(); if(sampleBuffer != NULL) { - for(uint32_t loop=0; loop < 8; loop++) + for(uint32_t loop=0; loop < 150; loop++) { uint32_t readReq = (TS_SAMPLE_BUFFER_SIZE * 0x100); //Collect Samples @@ -396,12 +427,15 @@ static void test_capture(file_t fd, uint32_t idx, uint8_t channelBitmap, uint16_ if(watch_bitslip) { + tsScopeState_t scopeState = {0}; bitslip_count = litepcie_readl(fd, CSR_ADC_HMCAD1520_BITSLIP_COUNT_ADDR); printf("Bitslip Snapshot: %lu\r\n", bitslip_count); dbg_monitor = litepcie_readl(fd, CSR_ADC_HMCAD1520_FRAME_DEBUG_ADDR); printf("FRAME Debug: 0x%08x\r\n", dbg_monitor); dbg_monitor = litepcie_readl(fd, CSR_ADC_HMCAD1520_RANGE_ADDR); printf("RANGE: 0x%08x\r\n", dbg_monitor); + thunderscopeStatusGet(tsHdl, &scopeState); + printf("Scope State Flags: 0x%08x\r\n", scopeState.flags); } } } @@ -576,6 +610,61 @@ static void flash_test(char* arg, file_t fd) } } +static void test_clock(uint32_t idx, bool refoutclk, bool refinclk, uint32_t refclkfreq) +{ + tsHandle_t tsHdl = thunderscopeOpen(idx, false); + bool firstStatus = false; + + if(tsHdl) + { +#ifdef _WIN32 + SetConsoleCtrlHandler(SigHandler, TRUE); +#else + struct sigaction signalHandler; + signalHandler.sa_handler = SigHandler; + sigemptyset(&signalHandler.sa_mask); + signalHandler.sa_flags = 0; + sigaction(SIGINT, &signalHandler, NULL); +#endif + if(refinclk) + { + printf("Setting Ref In Clock @ %u Hz\n", refclkfreq); + printf("\t Result: %i\n", thunderscopeRefClockSet(tsHdl, TS_REFCLK_IN, refclkfreq)); + } + else if(refoutclk) + { + printf("Setting Ref Out Clock @ %u Hz\n", refclkfreq); + printf("\t Result: %i\n", thunderscopeRefClockSet(tsHdl, TS_REFCLK_OUT, refclkfreq)); + } + + while (g_program_loop) + { + tsScopeState_t state = {0}; + thunderscopeStatusGet(tsHdl, &state); + // if(!firstStatus) + // { + // printf("\x1b[A\x1b[A\x1b[A\x1b[A\x1b[A\x1b[A\x1b[A\x1b[A\x1b[A\x1b[A"); + // firstStatus = true; + // } + printf("-------\r\n"); + printf("PLL Status:\r\n"); + printf("\tPLL LOCK - %01x\r\n", state.pll_lock); + printf("\tPLL HIGH - %01x\r\n", state.pll_high); + printf("\tPLL LOW - %01x\r\n", state.pll_low); + printf("\tPLL ALT - %01x\r\n", state.pll_alt); + printf("INPUT Clock Status:\r\n"); + printf("\tLocal Valid - %01x\r\n", state.local_osc_clk); + printf("\tREF IN Valid - %01x\r\n", state.ref_in_clk); + printf("\r\n-- PRESS CTRL+C TO STOP --\r\n"); + + std::cout.flush(); + std::this_thread::sleep_until(awake_time()); + } + + thunderscopeClose(tsHdl); + } +} + static void print_help(void) { printf("TS Test Util Usage:\r\n"); @@ -592,6 +681,9 @@ static void print_help(void) printf("\t\t -o Channel Offset [microvolt]\r\n"); printf("\t\t -a AC Couple\r\n"); printf("\t\t -t 50 Ohm termination\r\n"); + printf("\t refclk - run the PLL source with different clock configurations\r\n"); + printf("\t\t -i Set the Ref IN Clock frequency\r\n"); + printf("\t\t -r Set the Ref OUT Clock frequency\r\n"); } /* Main */ @@ -615,6 +707,9 @@ int main(int argc, char** argv) uint8_t term = 0; bool bitslip = false; bool mode12bit = false; + bool refInClk = false; + bool refOutClk = false; + uint32_t refclkFreq = 0; struct optparse_long argList[] = { {"dev", 'd', OPTPARSE_REQUIRED}, @@ -622,6 +717,8 @@ int main(int argc, char** argv) {"bw", 'b', OPTPARSE_REQUIRED}, {"voltsuv", 'v', OPTPARSE_REQUIRED}, {"offsetuv", 'o', OPTPARSE_REQUIRED}, + {"refinclk", 'i', OPTPARSE_REQUIRED}, + {"refoutclk",'r', OPTPARSE_REQUIRED}, {"ac", 'a', OPTPARSE_NONE}, {"term", 't', OPTPARSE_NONE}, {"bits", 's', OPTPARSE_NONE}, @@ -659,6 +756,16 @@ int main(int argc, char** argv) offset_uV = strtol(options.optarg, NULL, 0); argCount+=2; break; + case 'i': + refInClk = true; + refclkFreq = strtol(options.optarg, NULL, 0); + argCount+=2; + break; + case 'r': + refOutClk = true; + refclkFreq = strtol(options.optarg, NULL, 0); + argCount+=2; + break; case 'a': ac_couple = 1; argCount++; @@ -689,6 +796,29 @@ int main(int argc, char** argv) exit(EXIT_FAILURE); } + if(0 == strcmp(arg, "list")) + { + uint32_t i = 0; + tsDeviceInfo_t infos; + while(TS_STATUS_OK == thunderscopeListDevices(i, &infos)) + { + if(i==0) + { + printf("Found ThunderScope(s):\n"); + } + printf("\t%3d | Serial Number: %s\n", i, infos.serial_number); + printf("\t | HW Rev: 0x%x\n", infos.hw_id); + printf("\t | GW Rev: 0x%x\n", infos.gw_id); + printf("\t | LiteX Rev: 0x%x\n", infos.litex); + i++; + } + if(i == 0) + { + printf("No devices present\n"); + } + exit(EXIT_SUCCESS); + } + if(0 == strcmp(arg, "clk")) { mcp_clkgen_conf_t test_conf[1024]; @@ -788,13 +918,18 @@ int main(int argc, char** argv) // Setup Channel, record samples to buffer, save buffer to file else if(0 == strcmp(arg, "capture")) { - test_capture(fd, idx, channelBitmap, bandwidth, volt_scale_uV, offset_uV, ac_couple, term, bitslip, mode12bit); + test_capture(fd, idx, channelBitmap, bandwidth, volt_scale_uV, offset_uV, ac_couple, term, bitslip, mode12bit, refInClk, refOutClk, refclkFreq); } // Flash test else if(0 == strcmp(arg, "flash")) { flash_test(argv[argCount+1], fd); } + // Test REF Clock Modes + else if(0 == strcmp(arg, "clock")) + { + test_clock(idx, refOutClk, refInClk, refclkFreq); + } //Print Help else { diff --git a/example/tsControl.py b/example/tsControl.py index f8687b2..11c93fa 100755 --- a/example/tsControl.py +++ b/example/tsControl.py @@ -203,7 +203,7 @@ def reset_fields(self): # Create the main application window root = tk.Tk() root.title("Thunderscope Channel Tester") -root.geometry("600x450") # Set the window size +root.geometry("650x650") # Set the window size # Create an instance of TsDelegate delegate = TsDelegate(root) @@ -262,9 +262,10 @@ def create_status_tab(notebook, delegate): # Define the keys from tsScopeState_t keys = [ "adc_sample_rate", "adc_sample_bits", "adc_sample_resolution", "adc_lost_buffer_count", - "flags", "adc_state", "power_state", "pll_state", "afe_state", + "flags", "adc_state", "adc_sync", "power_state", "pll_state", "afe_state", "sys_health.temp_c", "sys_health.vcc_int", "sys_health.vcc_aux", - "sys_health.vcc_bram", "sys_health.frontend_power_good", "sys_health.acq_power_good" + "sys_health.vcc_bram", "sys_health.frontend_power_good", "sys_health.acq_power_good", + "local_osc_clk", "ref_in_clk", "pll_lock", "pll_low", "pll_high", "pll_alt" ] # Create labels and fields for each key diff --git a/include/thunderscope.h b/include/thunderscope.h index 896253c..2296a06 100644 --- a/include/thunderscope.h +++ b/include/thunderscope.h @@ -67,6 +67,16 @@ int32_t thunderscopeChannelConfigGet(tsHandle_t ts, uint32_t channel, tsChannelP */ int32_t thunderscopeChannelConfigSet(tsHandle_t ts, uint32_t channel, tsChannelParam_t* conf); +/** + * @brief Set the mode and frequency for the external Reference Clock + * + * @param ts Handle to the Thunderscope device + * @param mode Set the Clock IN/OUT mode + * @param refclk_freq Set the input clock frequency if in IN mode, or output frequency if in OUT mode + * @return int32_t TS_STATUS_OK if the reference clock was configured + */ +int32_t thunderscopeRefClockSet(tsHandle_t ts, tsRefClockMode_t mode, uint32_t refclk_freq); + /** * @brief Get the status for the Thunderscope device * diff --git a/include/ts_common.h b/include/ts_common.h index 5a77536..d945310 100644 --- a/include/ts_common.h +++ b/include/ts_common.h @@ -34,11 +34,14 @@ extern "C" { #define TS_HW_ID_VARIANT_MASK (1 << 8) #define TS_HW_ID_VALID_MASK (1 << 9) +#define TS_DEFUALT_CLKOUT_FREQ (10000000) //10 MHz + #define TS_GW_VERSION(major, minor, patch) ((((major) & 0xFFFF) << 16) + \ (((minor) & 0xFF) << 8) + \ (((patch) & 0x3F) << 1)) #define LITEX_VERSION(major, minor) ((((major) & 0xFFFF) << 16) + ((minor) & 0xFFFF)) + /** * @brief Opaque Handle to a Thunderscope device instance * @@ -58,6 +61,13 @@ typedef enum tsChannelTerm_e TS_TERM_50 = 1, } tsChannelTerm_t; +typedef enum tsRefClockMode_e +{ + TS_REFCLK_NONE = 0, + TS_REFCLK_OUT = 1, + TS_REFCLK_IN = 2 +} tsRefClockMode_t; + typedef struct tsDeviceInfo_s { uint32_t device_id; @@ -109,6 +119,12 @@ typedef struct tsScopeState_s uint8_t power_state:1; uint8_t pll_state:1; uint8_t afe_state:1; + uint8_t local_osc_clk:1; + uint8_t ref_in_clk:1; + uint8_t pll_lock:1; + uint8_t pll_low:1; + uint8_t pll_high:1; + uint8_t pll_alt:1; }; }; sysHealth_t sys_health; diff --git a/litepcie/CMakeLists.txt b/litepcie/CMakeLists.txt index 3ebb6f9..2d6c073 100644 --- a/litepcie/CMakeLists.txt +++ b/litepcie/CMakeLists.txt @@ -48,8 +48,8 @@ set_target_properties(litepcie PROPERTIES POSITION_INDEPENDENT_CODE 1) target_include_directories(litepcie PUBLIC public_h include) if(WIN32) - target_link_libraries(litepcie setupapi) + target_link_libraries(litepcie PRIVATE setupapi) elseif(APPLE) - target_link_libraries(litepcie "-framework IOKit") - target_link_libraries(litepcie "-framework CoreFoundation") + target_link_libraries(litepcie "$") + target_link_libraries(litepcie "$") endif() \ No newline at end of file diff --git a/src/mcp_clkgen.c b/src/mcp_clkgen.c index 637bee8..f9a34ca 100644 --- a/src/mcp_clkgen.c +++ b/src/mcp_clkgen.c @@ -59,6 +59,35 @@ int32_t mcp_clkgen_config(i2c_t device, const mcp_clkgen_conf_t* confData, uint3 return TS_STATUS_OK; } +int32_t mcp_clkgen_status(i2c_t device, const mcp_clkgen_status_t* statusData, uint32_t statusLen) +{ + int32_t status = 0; + + if(NULL == statusData) + { + //Error + return TS_STATUS_ERROR; + } + LOG_DEBUG("Status Regs:"); + + for(uint32_t i = 0; i < statusLen; i++) + { + uint8_t data[1] = {0}; + if(!i2c_read(device, (uint32_t)ZL302XX_READ_REG(statusData[i].addr), + data, 1, ZL302XX_ADDR_LEN)) + { + LOG_DEBUG("\t%06X (NACK!)", (uint32_t)ZL302XX_READ_REG(statusData[i].addr)); + return TS_STATUS_ERROR; + } + LOG_DEBUG("\t%06X : %02X", (uint32_t)ZL302XX_WRITE_REG(statusData[i].addr), data[0]); + + status |= (data[0] & statusData[i].mask) ? (1 << i) : 0; + } + + return status; +} + + void mcp_clkgen_regdump(i2c_t device, const mcp_clkgen_conf_t* confData, uint32_t confLen) { diff --git a/src/mcp_clkgen.h b/src/mcp_clkgen.h index 396631b..9baca15 100644 --- a/src/mcp_clkgen.h +++ b/src/mcp_clkgen.h @@ -33,6 +33,11 @@ typedef struct mcp_clkgen_conf_s { }; }mcp_clkgen_conf_t; +typedef struct mcp_clkgen_status_s { + uint16_t addr; + uint8_t mask; +} mcp_clkgen_status_t; + /** * @brief Configure the PLL with a list of registers. * @@ -43,6 +48,15 @@ typedef struct mcp_clkgen_conf_s { */ int32_t mcp_clkgen_config(i2c_t device, const mcp_clkgen_conf_t* confData, uint32_t confLen); +/** + * @brief Get a set of status flags for the PLL + * @param device I2C device for the zl30260/zl30250 + * @param statusData Array of status registers to read + * @param statusLen Length of the status array + * @return int32_t Bitfield of status flags, or TS_STATUS_ERROR on error + */ +int32_t mcp_clkgen_status(i2c_t device, const mcp_clkgen_status_t* statusData, uint32_t statusLen); + /** * @brief Debug function to print all register values and compare them to written values * diff --git a/src/mcp_zl3026x.c b/src/mcp_zl3026x.c index 9c9c861..c758152 100644 --- a/src/mcp_zl3026x.c +++ b/src/mcp_zl3026x.c @@ -32,7 +32,6 @@ static uint64_t mcp_zl3026x_selected_input_freq(zl3026x_clk_config_t *conf); int32_t mcp_zl3026x_build_config(mcp_clkgen_conf_t* confData, uint32_t len, zl3026x_clk_config_t conf) { int32_t calLen = 0; - // Reference App Note ZLAN-590 // https://ww1.microchip.com/downloads/aemDocuments/documents/TCG/ApplicationNotes/ApplicationNotes/ConfigurationSequenceZLAN-590.pdf @@ -82,10 +81,10 @@ int32_t mcp_zl3026x_build_config(mcp_clkgen_conf_t* confData, uint32_t len, zl30 { if(conf.in_clks[ch].enable) { - MCP_ADD_REG_WRITE(&confData[calLen], (0x0303+ch), (uint8_t)conf.in_clks[ch].input_divider); + LOG_DEBUG("Input Clock %d: Freq %llu Hz, Div %d", ch, conf.in_clks[ch].input_freq, conf.in_clks[ch].input_divider); + MCP_ADD_REG_WRITE(&confData[calLen], (0x0303+ch), (uint8_t)conf.in_clks[ch].input_divider | ZL3026X_VALTIME_DEFAULT); calLen++; in_ch_bitmap |= (1 << ch); - break; } } if(in_ch_bitmap == 0) @@ -197,6 +196,8 @@ int32_t mcp_zl3026x_build_config(mcp_clkgen_conf_t* confData, uint32_t len, zl30 } } + LOG_DEBUG("Calculated APLL Output Freq: %llu Hz", pll_out); + uint64_t pll_vco = 4200000000; uint32_t pll_int_div = pll_vco/pll_out; //validate pll_int_div 4-15 @@ -236,7 +237,12 @@ int32_t mcp_zl3026x_build_config(mcp_clkgen_conf_t* confData, uint32_t len, zl30 MCP_ADD_REG_WRITE(&confData[calLen], 0x0101, pll_int_div & 0x0F); //PLL Output Integer Divide calLen++; - MCP_ADD_REG_WRITE(&confData[calLen], 0x0102, (conf.input_select & 0x7)); //PLL Input + uint8_t apll_cr3 = (conf.input_select & 0x7); //PLL Input + if(conf.alternate_select != ZL3026X_INPUT_NONE) + { + apll_cr3 |= 0x80 | ((conf.alternate_select & 0x07) << 3); //Enable Input Monitoring and Alternate PLL Input + } + MCP_ADD_REG_WRITE(&confData[calLen], 0x0102, apll_cr3); //PLL Input calLen++; //PLL AFBDIV @@ -369,6 +375,7 @@ int32_t mcp_zl3026x_build_config(mcp_clkgen_conf_t* confData, uint32_t len, zl30 { if(conf.out_clks[ch].enable) { + LOG_DEBUG("Setting Output CLK %d to %llu Hz", ch, conf.out_clks[ch].output_freq); /** * The frequency of the clock from the medium-speed divider is divided by LSDIV+1. * @@ -382,32 +389,42 @@ int32_t mcp_zl3026x_build_config(mcp_clkgen_conf_t* confData, uint32_t len, zl30 uint8_t out_div, out_div_med = 1; out_div = 0x80; //Phase align output + uint32_t clksrc_freq = 0; if(conf.out_clks[ch].output_pll_select == ZL3026X_PLL_INT_DIV) { - if(conf.out_clks[ch].output_freq != pll_out) + clksrc_freq = pll_out; + } + else if(conf.out_clks[ch].output_pll_select == ZL3026X_PLL_BYPASS) + { + clksrc_freq = mcp_zl3026x_selected_input_freq(&conf); + } + + if(conf.out_clks[ch].output_freq != clksrc_freq) + { + out_divide = clksrc_freq / conf.out_clks[ch].output_freq; + // Note: output_freq = clksrc_freq / (div_med * div_low) + while(out_divide >= (1 << 7)) { - out_divide = pll_out / conf.out_clks[ch].output_freq; - // Note: output_freq = pll_out / (div_med * div_low) - while(out_divide >= (1 << 7)) + if(out_divide & 0x1) { - if(out_divide & 0x1) - { - LOG_ERROR("======== TODO: FIX OUTPUT DIVIDE ======="); - return TS_STATUS_ERROR; - } - out_divide >>= 1; - out_div_low <<= 1; + LOG_ERROR("======== TODO: FIX OUTPUT DIVIDE ======="); + return TS_STATUS_ERROR; } - - out_div_med = out_divide; - - out_div |= (out_div_med-1) & 0x7F; + out_divide >>= 1; + out_div_low <<= 1; } + + out_div_med = out_divide; + + out_div |= (out_div_med-1) & 0x7F; } + LOG_DEBUG("Setting Output CLK %d LDIV to %02X", ch, out_div_low); + LOG_DEBUG("Setting Output CLK %d CR1 to %02X", ch, out_div); MCP_ADD_REG_WRITE(&confData[calLen], 0x0200+(ch*0x10), out_div); calLen++; + LOG_DEBUG("Setting Output CLK %d mode to %02X", ch, conf.out_clks[ch].output_mode); MCP_ADD_REG_WRITE(&confData[calLen], 0x0201+(ch*0x10), conf.out_clks[ch].output_mode); calLen++; diff --git a/src/mcp_zl3026x.h b/src/mcp_zl3026x.h index 7111f6a..8bd40ca 100644 --- a/src/mcp_zl3026x.h +++ b/src/mcp_zl3026x.h @@ -27,12 +27,19 @@ extern "C" { #define ZL3026X_OUT_MDIV_MIN_CLK (375000000) +#define ZL3026X_INPUT_CLK_MIN (9720000) +#define ZL3026X_INPUT_CLK_MAX (156250000) + +#define ZL3026X_VALTIME_DEFAULT (0x0C) + + typedef enum zl3026x_input_e { ZL3026X_INPUT_IC1, ZL3026X_INPUT_IC2, ZL3026X_INPUT_IC3, ZL3026X_INPUT_XO, - ZL3026X_INPUT_XO_DBL + ZL3026X_INPUT_XO_DBL, + ZL3026X_INPUT_NONE } zl3026x_input_t; typedef enum zl3026x_input_div_e { @@ -85,6 +92,7 @@ typedef struct zl3026x_clk_config_s { zl3026x_input_clk_t in_clks[ZL3026X_NUM_INPUT_CLK]; zl3026x_output_clk_t out_clks[ZL3026X_NUM_OUTPUT_CLK]; zl3026x_input_t input_select; + zl3026x_input_t alternate_select; //TODO Add GPIO Config } zl3026x_clk_config_t; diff --git a/src/platform.c b/src/platform.c index 9f6ec88..53fab5c 100644 --- a/src/platform.c +++ b/src/platform.c @@ -52,6 +52,19 @@ const mcp_clkgen_conf_t ZL30260_CONF[] = { const uint32_t ZL30260_CONF_SIZE = sizeof(ZL30260_CONF)/sizeof(ZL30260_CONF[0]); +const mcp_clkgen_status_t ZL30260_STATUS[] = { + {.addr=0x004C, .mask=0x02}, //XA.XAV Status + {.addr=0x004D, .mask=0x02}, //IC1.ICV Status + {.addr=0x004E, .mask=0x02}, //IC2.ICV Status + {.addr=0x004F, .mask=0x02}, //IC3.ICV Status + {.addr=0x0048, .mask=0x04}, //APLL.ALK Status + {.addr=0x0048, .mask=0x40}, //APLL.AIFL Status + {.addr=0x0048, .mask=0x10}, //APLL.AIFH Status + {.addr=0x0048, .mask=0x01}, //APLL.SELREF Status +}; + +const uint32_t ZL30260_STATUS_SIZE = sizeof(ZL30260_STATUS)/sizeof(ZL30260_STATUS[0]); + const mcp_clkgen_conf_t ZL30250_CONF[] = { {.action=MCP_CLKGEN_WRITE_REG, .addr=0x0009, .value=0x02}, {.action=MCP_CLKGEN_WRITE_REG, .addr=0x0621, .value=0x08}, diff --git a/src/platform.h b/src/platform.h index 3e2201d..c97b290 100644 --- a/src/platform.h +++ b/src/platform.h @@ -94,22 +94,29 @@ extern const uint32_t ZL30250_CONF_SIZE; extern const mcp_clkgen_conf_t ZL30260_CONF[]; extern const uint32_t ZL30260_CONF_SIZE; +extern const mcp_clkgen_status_t ZL30260_STATUS[]; +extern const uint32_t ZL30260_STATUS_SIZE; + #ifdef TS_REV_3 #define TS_PLL_I2C_ADDR ZL30250_I2C_ADDR #define TS_PLL_CONF ZL30250_CONF #define TS_PLL_CONF_SIZE ZL30250_CONF_SIZE #else -#define TS_PLL_BUS_BETA CSR_I2CBUS_I2C0_PHY_SPEED_MODE_ADDR -#define TS_PLL_BUS_DEV CSR_I2CBUS_I2C1_PHY_SPEED_MODE_ADDR -#define TS_PLL_I2C_ADDR ZL30260_I2C_ADDR -#define TS_PLL_CONF ZL30260_CONF -#define TS_PLL_CONF_SIZE ZL30260_CONF_SIZE -#define TS_PLL_INPUT_IDX (1) -#define TS_PLL_INPUT_RATE (10000000) -#define TS_PLL_INPUT_SEL (ZL3026X_INPUT_IC2) - -#define TS_PLL_REFIN_IDX (0) -#define TS_PLL_REFIN_SEL (ZL3026X_INPUT_IC1) +#define TS_PLL_BUS_BETA CSR_I2CBUS_I2C0_PHY_SPEED_MODE_ADDR +#define TS_PLL_BUS_DEV CSR_I2CBUS_I2C1_PHY_SPEED_MODE_ADDR +#define TS_PLL_I2C_ADDR ZL30260_I2C_ADDR +#define TS_PLL_CONF ZL30260_CONF +#define TS_PLL_CONF_SIZE ZL30260_CONF_SIZE +#define TS_PLL_STATUS ZL30260_STATUS +#define TS_PLL_STATUS_LEN ZL30260_STATUS_SIZE +#define TS_PLL_LOCAL_OSC_IDX (1) +#define TS_PLL_LOCAL_OSC_RATE (10000000) +#define TS_PLL_LOCAL_OSC_SEL (ZL3026X_INPUT_IC2) + +#define TS_PLL_REFIN_IDX (0) +#define TS_PLL_REFIN_SEL (ZL3026X_INPUT_IC1) + +#define TS_PLL_INPUT_NONE_SEL (ZL3026X_INPUT_NONE) #define TS_PLL_REFOUT_CLK_IDX (0) #define TS_PLL_REFOUT_RATE_DEFAULT (10000000) @@ -120,6 +127,15 @@ extern const uint32_t ZL30260_CONF_SIZE; #define TS_PLL_SAMPLE_RATE_DEFAULT (1000000000) #define TS_PLL_SAMPLE_CLK_MODE (ZL3026X_OUT_DIFF) #define TS_PLL_SAMPLE_PLL_MODE (ZL3026X_PLL_INT_DIV) + +#define TS_PLL_STATUS_XA_VALID (0) +#define TS_PLL_STATUS_IC1_VALID (1) +#define TS_PLL_STATUS_IC2_VALID (2) +#define TS_PLL_STATUS_IC3_VALID (3) +#define TS_PLL_STATUS_APLL_LOCK (4) +#define TS_PLL_STATUS_APLL_LOW (5) +#define TS_PLL_STATUS_APLL_HIGH (6) +#define TS_PLL_STATUS_APLL_ALT (7) #endif #define TS_PLL_NRST_ADDR CSR_ADC_CONTROL_ADDR diff --git a/src/thunderscope.c b/src/thunderscope.c index bafd8b3..1471c42 100644 --- a/src/thunderscope.c +++ b/src/thunderscope.c @@ -217,6 +217,18 @@ int32_t thunderscopeChannelConfigSet(tsHandle_t ts, uint32_t channel, tsChannelP return TS_STATUS_ERROR; } +int32_t thunderscopeRefClockSet(tsHandle_t ts, tsRefClockMode_t mode, uint32_t refclk_freq) +{ + ts_inst_t* pInst = (ts_inst_t*)ts; + + if(pInst && pInst->initialized) + { + return ts_channel_ext_clock_config(pInst->pChannel, mode, refclk_freq); + } + + return TS_STATUS_ERROR; +} + int32_t thunderscopeStatusGet(tsHandle_t ts, tsScopeState_t* state) { ts_inst_t* pInst = (ts_inst_t*)ts; diff --git a/src/ts_channel.c b/src/ts_channel.c index 426e050..6f5029f 100644 --- a/src/ts_channel.c +++ b/src/ts_channel.c @@ -187,9 +187,13 @@ int32_t ts_channel_init(tsChannelHdl_t* pTsChannels, file_t ts) //Set I2C Clock i2c_rate_set(pChan->pll.clkGen, TS_I2C_CLK_RATE); - pChan->pll.clkConf.in_clks[TS_PLL_INPUT_IDX].enable = 1; - pChan->pll.clkConf.in_clks[TS_PLL_INPUT_IDX].input_freq = TS_PLL_INPUT_RATE; - pChan->pll.clkConf.input_select = TS_PLL_INPUT_SEL; + pChan->pll.clkConf.in_clks[TS_PLL_LOCAL_OSC_IDX].enable = 1; + pChan->pll.clkConf.in_clks[TS_PLL_LOCAL_OSC_IDX].input_freq = TS_PLL_LOCAL_OSC_RATE; + pChan->pll.clkConf.in_clks[TS_PLL_LOCAL_OSC_IDX].input_divider = 0; + pChan->pll.clkConf.input_select = TS_PLL_LOCAL_OSC_SEL; + pChan->pll.clkConf.alternate_select = TS_PLL_INPUT_NONE_SEL; + pChan->pll.clkConf.in_clks[TS_PLL_REFIN_IDX].enable = 0; + pChan->pll.clkConf.in_clks[TS_PLL_REFIN_IDX].input_divider = 0; pChan->pll.clkConf.out_clks[TS_PLL_REFOUT_CLK_IDX].enable = 1; pChan->pll.clkConf.out_clks[TS_PLL_REFOUT_CLK_IDX].output_freq = TS_PLL_REFOUT_RATE_DEFAULT; pChan->pll.clkConf.out_clks[TS_PLL_REFOUT_CLK_IDX].output_mode = TS_PLL_REFOUT_CLK_MODE; @@ -528,7 +532,22 @@ tsScopeState_t ts_channel_scope_status(tsChannelHdl_t tsChannels) //Update XADC values ts_channel_health_update(pTsHdl); - return ((ts_channel_t*)tsChannels)->status; + //Update Clock Status + int32_t clock_status = mcp_clkgen_status(pTsHdl->pll.clkGen, TS_PLL_STATUS, TS_PLL_STATUS_LEN); + if(clock_status < 0) + { + LOG_ERROR("Failed to read PLL Clock Status %d", clock_status); + } + else + { + pTsHdl->status.local_osc_clk = (clock_status & (1 << TS_PLL_STATUS_IC2_VALID)) ? 1:0; + pTsHdl->status.ref_in_clk = (clock_status & (1 << TS_PLL_STATUS_IC1_VALID)) ? 1:0; + pTsHdl->status.pll_lock = (clock_status & (1 << TS_PLL_STATUS_APLL_LOCK)) ? 1:0; + pTsHdl->status.pll_low = (clock_status & (1 << TS_PLL_STATUS_APLL_LOW)) ? 1:0; + pTsHdl->status.pll_high = (clock_status & (1 << TS_PLL_STATUS_APLL_HIGH)) ? 1:0; + pTsHdl->status.pll_alt = (clock_status & (1 << TS_PLL_STATUS_APLL_ALT)) ? 1:0; + } + return pTsHdl->status; } int32_t ts_channel_sample_rate_set(tsChannelHdl_t tsChannels, uint32_t rate, uint32_t resolution) @@ -615,6 +634,107 @@ int32_t ts_channel_sample_rate_set(tsChannelHdl_t tsChannels, uint32_t rate, uin return TS_STATUS_OK; } +int32_t ts_channel_ext_clock_config(tsChannelHdl_t tsChannels, tsRefClockMode_t mode, uint32_t refclk_freq) +{ + if(tsChannels == NULL) + { + return TS_STATUS_ERROR; + } + ts_channel_t* ts = (ts_channel_t*)tsChannels; + zl3026x_clk_config_t newConf = ts->pll.clkConf; + bool clkout_en = (mode == TS_REFCLK_OUT); + bool clkin_en = (mode == TS_REFCLK_IN); + uint8_t clkin_divider = 0; + uint32_t input_freq = TS_PLL_LOCAL_OSC_RATE; + + //Validate Settings + if (clkout_en && refclk_freq == 0) + { + LOG_ERROR("Invalid Clock Out Frequency, cannot be 0"); + return TS_INVALID_PARAM; + } + else if(clkin_en && refclk_freq < ZL3026X_INPUT_CLK_MIN) + { + LOG_ERROR("Invalid Clock Input Frequency %d Hz. Must be a minimum of %d Hz", refclk_freq, ZL3026X_INPUT_CLK_MIN); + return TS_INVALID_PARAM; + } + + if(clkin_en) + { + //Divide Input Clock Frequency if needed + while((refclk_freq / (1UL << clkin_divider)) > ZL3026X_INPUT_CLK_MAX) + { + clkin_divider++; + if(clkin_divider > ZL3026X_IN_DIV_8) + { + LOG_ERROR("Unable to configure external clock input frequency %d Hz", refclk_freq); + return TS_INVALID_PARAM; + } + } + + //Set Input Clock Configuration + newConf.in_clks[TS_PLL_REFIN_IDX].enable = 1; + newConf.in_clks[TS_PLL_REFIN_IDX].input_freq = refclk_freq / (1 << clkin_divider); + newConf.in_clks[TS_PLL_REFIN_IDX].input_divider = (zl3026x_input_div_t)clkin_divider; + newConf.input_select = TS_PLL_REFIN_SEL; + newConf.alternate_select = TS_PLL_LOCAL_OSC_SEL; + + //Input frequency on bypass path + input_freq = newConf.in_clks[TS_PLL_REFIN_IDX].input_freq; + } + else + { + //Use Internal Reference Clock + newConf.in_clks[TS_PLL_REFIN_IDX].enable = 0; + newConf.input_select = TS_PLL_LOCAL_OSC_SEL; + newConf.alternate_select = TS_PLL_INPUT_NONE_SEL; + } + + //Set Output Clock Configuration + if(clkout_en) + { + //Validate Output Clock Frequency + if(refclk_freq > ZL3026X_MAX_PLL_OUT) + { + LOG_ERROR("Invalid Clock Output Frequency %d Hz. Must be a maximum of %d Hz", refclk_freq, ZL3026X_MAX_PLL_OUT); + return TS_INVALID_PARAM; + } + + newConf.out_clks[TS_PLL_REFOUT_CLK_IDX].enable = 1; + newConf.out_clks[TS_PLL_REFOUT_CLK_IDX].output_freq = refclk_freq; + if(refclk_freq > input_freq) + { + newConf.out_clks[TS_PLL_REFOUT_CLK_IDX].output_pll_select = ZL3026X_PLL_INT_DIV; + } + else + { + newConf.out_clks[TS_PLL_REFOUT_CLK_IDX].output_pll_select = ZL3026X_PLL_BYPASS; + } + } + else + { + //Disable Output Ref Clock + newConf.out_clks[TS_PLL_REFOUT_CLK_IDX].enable = 0; + } + + mcp_clkgen_conf_t clk_regs[MCP_CLKGEN_ARR_MAX_LEN] = {0}; + int32_t clk_len = mcp_zl3026x_build_config(clk_regs, MCP_CLKGEN_ARR_MAX_LEN, newConf); + if(clk_len > 0) + { + if(TS_STATUS_OK != mcp_clkgen_config(ts->pll.clkGen, clk_regs, clk_len)) + { + return TS_STATUS_ERROR; + } + ts->pll.clkConf = newConf; + } + else + { + LOG_ERROR("Failed to generate PLL Configuration: %d", clk_len); + return clk_len; + } + + return TS_STATUS_OK; +} int32_t ts_channel_calibration_set(tsChannelHdl_t tsChannels, uint32_t chanIdx, tsChannelCalibration_t* cal) { diff --git a/src/ts_channel.h b/src/ts_channel.h index 0198e5d..32cc0d0 100644 --- a/src/ts_channel.h +++ b/src/ts_channel.h @@ -86,6 +86,16 @@ tsScopeState_t ts_channel_scope_status(tsChannelHdl_t tsChannels); */ int32_t ts_channel_sample_rate_set(tsChannelHdl_t tsChannels, uint32_t rate, uint32_t resolution); +/** + * @brief Configure the Clock Generator Reference Clock In/Out + * + * @param tsChannels Thunderscope Channel handle + * @param mode Set the Clock IN/OUT mode + * @param refclk_freq Set the input clock frequency if in IN mode, or output frequency if in OUT mode + * @return int32_t TS_STATUS_OK on success, else TS_STATUS_ERROR + */ +int32_t ts_channel_ext_clock_config(tsChannelHdl_t tsChannels, tsRefClockMode_t mode, uint32_t refclk_freq); + /** * @brief Set the calibration parameters for a channel *