From 1af5126cd1ecbbb6ecbc7046a506bb037f7a163e Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Fri, 28 Nov 2025 12:11:57 -0500 Subject: [PATCH 1/8] Add api to set Ref Clock IN/OUT --- include/thunderscope.h | 10 ++++ include/ts_common.h | 10 ++++ src/mcp_zl3026x.h | 4 ++ src/platform.h | 22 ++++----- src/thunderscope.c | 12 +++++ src/ts_channel.c | 110 +++++++++++++++++++++++++++++++++++++++-- src/ts_channel.h | 10 ++++ 7 files changed, 164 insertions(+), 14 deletions(-) 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..cc193c4 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; diff --git a/src/mcp_zl3026x.h b/src/mcp_zl3026x.h index 7111f6a..4a6c716 100644 --- a/src/mcp_zl3026x.h +++ b/src/mcp_zl3026x.h @@ -27,6 +27,10 @@ extern "C" { #define ZL3026X_OUT_MDIV_MIN_CLK (375000000) +#define ZL3026X_INPUT_CLK_MIN (9720000) +#define ZL3026X_INPUT_CLK_MAX (156250000) + + typedef enum zl3026x_input_e { ZL3026X_INPUT_IC1, ZL3026X_INPUT_IC2, diff --git a/src/platform.h b/src/platform.h index 3e2201d..470fb80 100644 --- a/src/platform.h +++ b/src/platform.h @@ -99,17 +99,17 @@ extern const uint32_t ZL30260_CONF_SIZE; #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_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_REFOUT_CLK_IDX (0) #define TS_PLL_REFOUT_RATE_DEFAULT (10000000) diff --git a/src/thunderscope.c b/src/thunderscope.c index bafd8b3..cb642ca 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 thunderscopeChannelRefClockSet(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..e8608a8 100644 --- a/src/ts_channel.c +++ b/src/ts_channel.c @@ -187,9 +187,11 @@ 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 = 1; + pChan->pll.clkConf.input_select = TS_PLL_LOCAL_OSC_SEL; + pChan->pll.clkConf.in_clks[TS_PLL_REFIN_IDX].enable = 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; @@ -615,6 +617,108 @@ 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 = 1; + 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 / (1 << 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_LOCAL_OSC_IDX].enable = 0; + 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; + + //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_LOCAL_OSC_IDX].enable = 1; + newConf.in_clks[TS_PLL_REFIN_IDX].enable = 0; + newConf.input_select = TS_PLL_LOCAL_OSC_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 * From 655f62918c556ced6698b39e37ea57e1d9245eeb Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Fri, 28 Nov 2025 22:33:01 -0500 Subject: [PATCH 2/8] Fix clock dividers and add Ref Clk settings for test app --- example/thunderscope_test.cpp | 30 +++++++++++++++++++++++-- src/mcp_zl3026x.c | 41 ++++++++++++++++++++--------------- src/thunderscope.c | 2 +- src/ts_channel.c | 7 +++--- 4 files changed, 56 insertions(+), 24 deletions(-) diff --git a/example/thunderscope_test.cpp b/example/thunderscope_test.cpp index e4f270e..cb2ba53 100644 --- a/example/thunderscope_test.cpp +++ b/example/thunderscope_test.cpp @@ -306,7 +306,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 +317,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; @@ -615,6 +626,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 +636,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 +675,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++; @@ -788,7 +814,7 @@ 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")) diff --git a/src/mcp_zl3026x.c b/src/mcp_zl3026x.c index 9c9c861..1faa476 100644 --- a/src/mcp_zl3026x.c +++ b/src/mcp_zl3026x.c @@ -32,7 +32,7 @@ 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; - + uint32_t scaled_in_freq = 0; // Reference App Note ZLAN-590 // https://ww1.microchip.com/downloads/aemDocuments/documents/TCG/ApplicationNotes/ApplicationNotes/ConfigurationSequenceZLAN-590.pdf @@ -85,6 +85,7 @@ int32_t mcp_zl3026x_build_config(mcp_clkgen_conf_t* confData, uint32_t len, zl30 MCP_ADD_REG_WRITE(&confData[calLen], (0x0303+ch), (uint8_t)conf.in_clks[ch].input_divider); calLen++; in_ch_bitmap |= (1 << ch); + scaled_in_freq = conf.in_clks[ch].input_freq / (1 << conf.in_clks[ch].input_divider); break; } } @@ -197,6 +198,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 @@ -369,6 +372,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 +386,35 @@ 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 - if(conf.out_clks[ch].output_pll_select == ZL3026X_PLL_INT_DIV) + if(((conf.out_clks[ch].output_pll_select == ZL3026X_PLL_INT_DIV) && + (conf.out_clks[ch].output_freq != pll_out)) || + ((conf.out_clks[ch].output_pll_select == ZL3026X_PLL_BYPASS) && + (conf.out_clks[ch].output_freq != scaled_in_freq))) { - if(conf.out_clks[ch].output_freq != pll_out) + out_divide = pll_out / conf.out_clks[ch].output_freq; + // Note: output_freq = pll_out / (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/thunderscope.c b/src/thunderscope.c index cb642ca..1471c42 100644 --- a/src/thunderscope.c +++ b/src/thunderscope.c @@ -217,7 +217,7 @@ int32_t thunderscopeChannelConfigSet(tsHandle_t ts, uint32_t channel, tsChannelP return TS_STATUS_ERROR; } -int32_t thunderscopeChannelRefClockSet(tsHandle_t ts, tsRefClockMode_t mode, uint32_t refclk_freq) +int32_t thunderscopeRefClockSet(tsHandle_t ts, tsRefClockMode_t mode, uint32_t refclk_freq) { ts_inst_t* pInst = (ts_inst_t*)ts; diff --git a/src/ts_channel.c b/src/ts_channel.c index e8608a8..080bb20 100644 --- a/src/ts_channel.c +++ b/src/ts_channel.c @@ -189,7 +189,7 @@ int32_t ts_channel_init(tsChannelHdl_t* pTsChannels, file_t ts) 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 = 1; + 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.in_clks[TS_PLL_REFIN_IDX].enable = 0; pChan->pll.clkConf.out_clks[TS_PLL_REFOUT_CLK_IDX].enable = 1; @@ -627,7 +627,7 @@ int32_t ts_channel_ext_clock_config(tsChannelHdl_t tsChannels, tsRefClockMode_t 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 = 1; + uint8_t clkin_divider = 0; uint32_t input_freq = TS_PLL_LOCAL_OSC_RATE; //Validate Settings @@ -645,7 +645,7 @@ int32_t ts_channel_ext_clock_config(tsChannelHdl_t tsChannels, tsRefClockMode_t if(clkin_en) { //Divide Input Clock Frequency if needed - while((refclk_freq / (1 << clkin_divider)) > ZL3026X_INPUT_CLK_MAX) + while((refclk_freq / (1UL << clkin_divider)) > ZL3026X_INPUT_CLK_MAX) { clkin_divider++; if(clkin_divider > ZL3026X_IN_DIV_8) @@ -671,7 +671,6 @@ int32_t ts_channel_ext_clock_config(tsChannelHdl_t tsChannels, tsRefClockMode_t newConf.in_clks[TS_PLL_LOCAL_OSC_IDX].enable = 1; newConf.in_clks[TS_PLL_REFIN_IDX].enable = 0; newConf.input_select = TS_PLL_LOCAL_OSC_SEL; - } //Set Output Clock Configuration From 6fa70afe85011b22f9246038a2a8c27a3f22f226 Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Fri, 28 Nov 2025 23:00:13 -0500 Subject: [PATCH 3/8] Divide output clock from the correct source frequency --- src/mcp_zl3026x.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/mcp_zl3026x.c b/src/mcp_zl3026x.c index 1faa476..ab020d9 100644 --- a/src/mcp_zl3026x.c +++ b/src/mcp_zl3026x.c @@ -386,13 +386,20 @@ 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 - if(((conf.out_clks[ch].output_pll_select == ZL3026X_PLL_INT_DIV) && - (conf.out_clks[ch].output_freq != pll_out)) || - ((conf.out_clks[ch].output_pll_select == ZL3026X_PLL_BYPASS) && - (conf.out_clks[ch].output_freq != scaled_in_freq))) + uint32_t clksrc_freq = 0; + if(conf.out_clks[ch].output_pll_select == ZL3026X_PLL_INT_DIV) { - out_divide = pll_out / conf.out_clks[ch].output_freq; - // Note: output_freq = pll_out / (div_med * div_low) + clksrc_freq = pll_out; + } + else if(conf.out_clks[ch].output_pll_select == ZL3026X_PLL_BYPASS) + { + clksrc_freq = scaled_in_freq; + } + + 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)) { if(out_divide & 0x1) From 5cb78422b107f7287395fe1e141f7f77f7d252cd Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Sat, 29 Nov 2025 12:06:22 -0500 Subject: [PATCH 4/8] Fix typo in test app --- CMakeLists.txt | 2 -- example/thunderscope_test.cpp | 2 +- src/mcp_zl3026x.c | 1 + 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 89a8d84..d07df18 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,7 +29,6 @@ FetchContent_Declare( json_c GIT_REPOSITORY https://github.com/json-c/json-c GIT_TAG 2372e9518e6ba95b48d37ec162bc7d93b297b52f - FIND_PACKAGE_ARGS NAMES json-c ) set(BUILD_SHARED_LIBS OFF) @@ -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) diff --git a/example/thunderscope_test.cpp b/example/thunderscope_test.cpp index cb2ba53..f305434 100644 --- a/example/thunderscope_test.cpp +++ b/example/thunderscope_test.cpp @@ -322,7 +322,7 @@ static void test_capture(file_t fd, uint32_t idx, uint8_t channelBitmap, uint16_ printf("Setting Ref In Clock @ %u Hz\n", refclkFreq); printf("\t Result: %i\n", thunderscopeRefClockSet(tsHdl, TS_REFCLK_IN, refclkFreq)); } - else if(outRefClk); + else if(outRefClk) { printf("Setting Ref Out Clock @ %u Hz\n", refclkFreq); printf("\t Result: %i\n", thunderscopeRefClockSet(tsHdl, TS_REFCLK_OUT, refclkFreq)); diff --git a/src/mcp_zl3026x.c b/src/mcp_zl3026x.c index ab020d9..ef6bc39 100644 --- a/src/mcp_zl3026x.c +++ b/src/mcp_zl3026x.c @@ -82,6 +82,7 @@ int32_t mcp_zl3026x_build_config(mcp_clkgen_conf_t* confData, uint32_t len, zl30 { if(conf.in_clks[ch].enable) { + LOG_DEBUG("Input Clock %d: Freq %u 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); calLen++; in_ch_bitmap |= (1 << ch); From 9980b7e4cfb952699bd270a74a83cd49263cc212 Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Sat, 29 Nov 2025 17:41:16 -0500 Subject: [PATCH 5/8] Add alternate input clock configuration and PLL clock status flags --- bindings/python/tslitex.pxd | 6 ++++++ example/tsControl.py | 3 ++- include/ts_common.h | 6 ++++++ src/mcp_clkgen.c | 29 +++++++++++++++++++++++++++++ src/mcp_clkgen.h | 14 ++++++++++++++ src/mcp_zl3026x.c | 9 +++++++-- src/mcp_zl3026x.h | 6 +++++- src/platform.c | 13 +++++++++++++ src/platform.h | 16 ++++++++++++++++ src/ts_channel.c | 22 +++++++++++++++++++--- 10 files changed, 117 insertions(+), 7 deletions(-) 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/tsControl.py b/example/tsControl.py index f8687b2..a427691 100755 --- a/example/tsControl.py +++ b/example/tsControl.py @@ -264,7 +264,8 @@ def create_status_tab(notebook, delegate): "adc_sample_rate", "adc_sample_bits", "adc_sample_resolution", "adc_lost_buffer_count", "flags", "adc_state", "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/ts_common.h b/include/ts_common.h index cc193c4..d945310 100644 --- a/include/ts_common.h +++ b/include/ts_common.h @@ -119,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/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 ef6bc39..f5560c9 100644 --- a/src/mcp_zl3026x.c +++ b/src/mcp_zl3026x.c @@ -83,7 +83,7 @@ int32_t mcp_zl3026x_build_config(mcp_clkgen_conf_t* confData, uint32_t len, zl30 if(conf.in_clks[ch].enable) { LOG_DEBUG("Input Clock %d: Freq %u 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); + 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); scaled_in_freq = conf.in_clks[ch].input_freq / (1 << conf.in_clks[ch].input_divider); @@ -240,7 +240,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 diff --git a/src/mcp_zl3026x.h b/src/mcp_zl3026x.h index 4a6c716..8bd40ca 100644 --- a/src/mcp_zl3026x.h +++ b/src/mcp_zl3026x.h @@ -30,13 +30,16 @@ extern "C" { #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 { @@ -89,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 470fb80..c97b290 100644 --- a/src/platform.h +++ b/src/platform.h @@ -94,6 +94,9 @@ 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 @@ -104,6 +107,8 @@ extern const uint32_t ZL30260_CONF_SIZE; #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) @@ -111,6 +116,8 @@ extern const uint32_t ZL30260_CONF_SIZE; #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) #define TS_PLL_REFOUT_CLK_MODE (ZL3026X_OUT_CMOS_P) @@ -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/ts_channel.c b/src/ts_channel.c index 080bb20..32f48bf 100644 --- a/src/ts_channel.c +++ b/src/ts_channel.c @@ -191,6 +191,7 @@ int32_t ts_channel_init(tsChannelHdl_t* pTsChannels, file_t ts) 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.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; @@ -530,7 +531,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_LOCAL_OSC_IDX)) ? 1:0; + pTsHdl->status.ref_in_clk = (clock_status & (1 << TS_PLL_REFIN_IDX)) ? 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) @@ -656,11 +672,11 @@ int32_t ts_channel_ext_clock_config(tsChannelHdl_t tsChannels, tsRefClockMode_t } //Set Input Clock Configuration - newConf.in_clks[TS_PLL_LOCAL_OSC_IDX].enable = 0; 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; @@ -668,9 +684,9 @@ int32_t ts_channel_ext_clock_config(tsChannelHdl_t tsChannels, tsRefClockMode_t else { //Use Internal Reference Clock - newConf.in_clks[TS_PLL_LOCAL_OSC_IDX].enable = 1; 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 From e9bdea97d82aee9e379c97f5ccea573d4b084238 Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Sat, 29 Nov 2025 18:43:30 -0500 Subject: [PATCH 6/8] Use right status bits for clock inputs --- src/ts_channel.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ts_channel.c b/src/ts_channel.c index 32f48bf..99f105a 100644 --- a/src/ts_channel.c +++ b/src/ts_channel.c @@ -539,8 +539,8 @@ tsScopeState_t ts_channel_scope_status(tsChannelHdl_t tsChannels) } else { - pTsHdl->status.local_osc_clk = (clock_status & (1 << TS_PLL_LOCAL_OSC_IDX)) ? 1:0; - pTsHdl->status.ref_in_clk = (clock_status & (1 << TS_PLL_REFIN_IDX)) ? 1:0; + 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; From 7a8e591372f617ddb427fe65b68ec5839acbba94 Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Sun, 30 Nov 2025 02:33:53 -0500 Subject: [PATCH 7/8] Fix multiple clocks. Add clock test function. --- example/thunderscope_test.cpp | 111 +++++++++++++++++++++++++++++++++- example/tsControl.py | 2 +- src/mcp_zl3026x.c | 7 +-- src/ts_channel.c | 1 + 4 files changed, 114 insertions(+), 7 deletions(-) diff --git a/example/thunderscope_test.cpp b/example/thunderscope_test.cpp index f305434..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); @@ -389,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 @@ -407,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); } } } @@ -587,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"); @@ -603,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 */ @@ -715,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]; @@ -821,6 +925,11 @@ int main(int argc, char** argv) { 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 a427691..4eb81cd 100755 --- a/example/tsControl.py +++ b/example/tsControl.py @@ -262,7 +262,7 @@ 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", "local_osc_clk", "ref_in_clk", "pll_lock", "pll_low", "pll_high", "pll_alt" diff --git a/src/mcp_zl3026x.c b/src/mcp_zl3026x.c index f5560c9..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; - uint32_t scaled_in_freq = 0; // Reference App Note ZLAN-590 // https://ww1.microchip.com/downloads/aemDocuments/documents/TCG/ApplicationNotes/ApplicationNotes/ConfigurationSequenceZLAN-590.pdf @@ -82,12 +81,10 @@ int32_t mcp_zl3026x_build_config(mcp_clkgen_conf_t* confData, uint32_t len, zl30 { if(conf.in_clks[ch].enable) { - LOG_DEBUG("Input Clock %d: Freq %u Hz, Div %d", ch, conf.in_clks[ch].input_freq, 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); - scaled_in_freq = conf.in_clks[ch].input_freq / (1 << conf.in_clks[ch].input_divider); - break; } } if(in_ch_bitmap == 0) @@ -399,7 +396,7 @@ int32_t mcp_zl3026x_build_config(mcp_clkgen_conf_t* confData, uint32_t len, zl30 } else if(conf.out_clks[ch].output_pll_select == ZL3026X_PLL_BYPASS) { - clksrc_freq = scaled_in_freq; + clksrc_freq = mcp_zl3026x_selected_input_freq(&conf); } if(conf.out_clks[ch].output_freq != clksrc_freq) diff --git a/src/ts_channel.c b/src/ts_channel.c index 99f105a..6f5029f 100644 --- a/src/ts_channel.c +++ b/src/ts_channel.c @@ -193,6 +193,7 @@ int32_t ts_channel_init(tsChannelHdl_t* pTsChannels, file_t ts) 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; From c173b2cdccad8ee37b7fde39a135b461237aabf6 Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Sun, 30 Nov 2025 17:09:20 -0500 Subject: [PATCH 8/8] Update linking for py binding library --- CMakeLists.txt | 31 +++++++++++++++++++++++-------- bindings/python/CMakeLists.txt | 13 ++++++++----- example/tsControl.py | 2 +- litepcie/CMakeLists.txt | 6 +++--- 4 files changed, 35 insertions(+), 17 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d07df18..4a69997 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,11 +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 ) - -set(BUILD_SHARED_LIBS OFF) +set(JSON_C_BUILD_APPS OFF) +set(JSON_C_BUILD_SHARED_LIBS OFF) FetchContent_MakeAvailable(json_c) # zlib Library @@ -129,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) @@ -153,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/example/tsControl.py b/example/tsControl.py index 4eb81cd..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) 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