| // Copyright lowRISC contributors. | |
| // Licensed under the Apache License, Version 2.0, see LICENSE for details. | |
| // SPDX-License-Identifier: Apache-2.0 | |
| #include "sw/device/lib/testing/clkmgr_testutils.h" | |
| #include "sw/device/lib/base/math.h" | |
| #include "sw/device/lib/dif/dif_clkmgr.h" | |
| static const char *measure_clock_names[kDifClkmgrMeasureClockUsb + 1] = { | |
| "io_clk", "io_div2_clk", "io_div4_clk", "main_clk", "usb_clk"}; | |
| // `extern` declarations to give the inline functions in the | |
| // corresponding header a link location. | |
| extern bool clkmgr_testutils_get_trans_clock_status( | |
| const dif_clkmgr_t *clkmgr, dif_clkmgr_hintable_clock_t clock); | |
| extern void clkmgr_testutils_check_trans_clock_gating( | |
| const dif_clkmgr_t *clkmgr, dif_clkmgr_hintable_clock_t clock, | |
| bool exp_clock_enabled, uint32_t timeout_usec); | |
| // The thresholds are encoded as | |
| // - max = count + variability | |
| // - min = count - variability | |
| typedef struct expected_count_info { | |
| uint32_t count; | |
| uint32_t variability; | |
| } expected_count_info_t; | |
| // The expected counts when jitter is disabled. | |
| static expected_count_info_t kNoJitterCountInfos[kDifClkmgrMeasureClockUsb + 1]; | |
| // The expected counts when jitter is enabled. | |
| static expected_count_info_t kJitterCountInfos[kDifClkmgrMeasureClockUsb + 1]; | |
| static uint32_t cast_safely(uint64_t val) { | |
| CHECK(val <= UINT32_MAX); | |
| return (uint32_t)val; | |
| } | |
| void initialize_expected_counts() { | |
| // The expected counts depend on the device, per sw/device/lib/arch/device.h. | |
| // Notice the ratios are small enough to fit a uint32_t, even if the Hz number | |
| // is in uint64_t. | |
| const uint32_t kDeviceCpuCount = | |
| cast_safely(udiv64_slow(kClockFreqCpuHz, kClockFreqAonHz, | |
| /*rem_out=*/NULL)); | |
| const uint32_t kDeviceIoCount = | |
| cast_safely(udiv64_slow(kClockFreqPeripheralHz, kClockFreqAonHz, | |
| /*rem_out=*/NULL) * | |
| 4); | |
| const uint32_t kDeviceIoDiv2Count = | |
| cast_safely(udiv64_slow(kClockFreqPeripheralHz, kClockFreqAonHz, | |
| /*rem_out=*/NULL) * | |
| 2); | |
| const uint32_t kDeviceIoDiv4Count = | |
| cast_safely(udiv64_slow(kClockFreqPeripheralHz, kClockFreqAonHz, | |
| /*rem_out=*/NULL)); | |
| const uint32_t kDeviceUsbCount = | |
| cast_safely(udiv64_slow(kClockFreqUsbHz, kClockFreqAonHz, | |
| /*rem_out=*/NULL)); | |
| // The expected counts are derived from the ratios of the frequencies of the | |
| // various clocks to the AON clock. For example, 48 Mhz / 200 kHz = 240, so | |
| // we set count to 239 and variability to 1, meaning the max threshold is 240, | |
| // and the min to 238. | |
| kNoJitterCountInfos[kDifClkmgrMeasureClockIo] = | |
| (expected_count_info_t){.count = kDeviceIoCount - 1, .variability = 1}; | |
| kNoJitterCountInfos[kDifClkmgrMeasureClockIoDiv2] = (expected_count_info_t){ | |
| .count = kDeviceIoDiv2Count - 1, .variability = 1}; | |
| kNoJitterCountInfos[kDifClkmgrMeasureClockIoDiv4] = (expected_count_info_t){ | |
| .count = kDeviceIoDiv4Count - 1, .variability = 1}; | |
| kNoJitterCountInfos[kDifClkmgrMeasureClockMain] = | |
| (expected_count_info_t){.count = kDeviceCpuCount - 1, .variability = 1}; | |
| kNoJitterCountInfos[kDifClkmgrMeasureClockUsb] = | |
| (expected_count_info_t){.count = kDeviceUsbCount - 1, .variability = 1}; | |
| // If jitter is enabled the low threshold should be up to 20% lower, so | |
| // the variability is set to 0.1 * max_count, and count as max - 0.1 * max. | |
| kJitterCountInfos[kDifClkmgrMeasureClockIo] = | |
| (expected_count_info_t){.count = kDeviceIoCount - kDeviceIoCount / 10, | |
| .variability = kDeviceIoCount / 10}; | |
| kJitterCountInfos[kDifClkmgrMeasureClockIoDiv2] = (expected_count_info_t){ | |
| .count = kDeviceIoDiv2Count - kDeviceIoDiv2Count / 10, | |
| .variability = kDeviceIoDiv2Count / 10}; | |
| kJitterCountInfos[kDifClkmgrMeasureClockIoDiv4] = (expected_count_info_t){ | |
| .count = kDeviceIoDiv4Count - kDeviceIoDiv4Count / 10, | |
| .variability = kDeviceIoDiv4Count}; | |
| kJitterCountInfos[kDifClkmgrMeasureClockMain] = | |
| (expected_count_info_t){.count = kDeviceCpuCount - kDeviceCpuCount / 10, | |
| .variability = kDeviceCpuCount / 10}; | |
| kJitterCountInfos[kDifClkmgrMeasureClockUsb] = | |
| (expected_count_info_t){.count = kDeviceUsbCount - kDeviceUsbCount / 10, | |
| .variability = kDeviceUsbCount / 10}; | |
| } | |
| const char *clkmgr_testutils_measurement_name( | |
| dif_clkmgr_measure_clock_t clock) { | |
| switch (clock) { | |
| kDifClkmgrMeasureClockIo: | |
| return "io"; | |
| kDifClkmgrMeasureClockIoDiv2: | |
| return "io_div2"; | |
| kDifClkmgrMeasureClockIoDiv4: | |
| return "io_div4"; | |
| kDifClkmgrMeasureClockMain: | |
| return "main"; | |
| kDifClkmgrMeasureClockUsb: | |
| return "usb"; | |
| default: | |
| LOG_ERROR("Unexpected clock measurement %d", clock); | |
| } | |
| return "unexpected clock"; | |
| } | |
| void clkmgr_testutils_enable_clock_count(const dif_clkmgr_t *clkmgr, | |
| dif_clkmgr_measure_clock_t clock, | |
| uint32_t lo_threshold, | |
| uint32_t hi_threshold) { | |
| LOG_INFO("Enabling clock count measurement for %s(%d) lo %d hi %d", | |
| measure_clock_names[clock], clock, lo_threshold, hi_threshold); | |
| CHECK_DIF_OK(dif_clkmgr_enable_measure_counts(clkmgr, clock, lo_threshold, | |
| hi_threshold)); | |
| } | |
| void clkmgr_testutils_enable_clock_counts_with_expected_thresholds( | |
| const dif_clkmgr_t *clkmgr, bool jitter_enabled, bool external_clk, | |
| bool low_speed) { | |
| static bool counts_initialized = false; | |
| if (!counts_initialized) { | |
| initialize_expected_counts(); | |
| counts_initialized = true; | |
| } | |
| CHECK(!(external_clk && jitter_enabled)); | |
| for (int clk = 0; clk < ARRAYSIZE(kNoJitterCountInfos); ++clk) { | |
| const expected_count_info_t *count_info; | |
| if (jitter_enabled) { | |
| count_info = &kJitterCountInfos[clk]; | |
| } else if (external_clk) { | |
| if (low_speed) { | |
| if (clk == kDifClkmgrMeasureClockIo || | |
| clk == kDifClkmgrMeasureClockMain) { | |
| count_info = &kNoJitterCountInfos[kDifClkmgrMeasureClockIoDiv2]; | |
| } else { | |
| count_info = &kNoJitterCountInfos[clk]; | |
| } | |
| } else { | |
| if (clk == kDifClkmgrMeasureClockMain) { | |
| count_info = &kNoJitterCountInfos[kDifClkmgrMeasureClockIo]; | |
| } else { | |
| count_info = &kNoJitterCountInfos[clk]; | |
| } | |
| } | |
| } else { | |
| count_info = &kNoJitterCountInfos[clk]; | |
| } | |
| clkmgr_testutils_enable_clock_count( | |
| clkmgr, (dif_clkmgr_measure_clock_t)clk, | |
| count_info->count - count_info->variability, | |
| count_info->count + count_info->variability); | |
| } | |
| } | |
| bool clkmgr_testutils_check_measurement_enables(const dif_clkmgr_t *clkmgr, | |
| dif_toggle_t expected_status) { | |
| bool success = true; | |
| for (int i = kDifClkmgrMeasureClockIo; i <= kDifClkmgrMeasureClockUsb; ++i) { | |
| dif_clkmgr_measure_clock_t clock = (dif_clkmgr_measure_clock_t)i; | |
| dif_toggle_t actual_status; | |
| CHECK_DIF_OK( | |
| dif_clkmgr_measure_counts_get_enable(clkmgr, clock, &actual_status)); | |
| if (actual_status != expected_status) { | |
| LOG_INFO("Unexpected enable for clock %d: expected %s", i, | |
| (expected_status == kDifToggleEnabled ? "enabled" : "disabled")); | |
| success = false; | |
| } | |
| } | |
| return success; | |
| } | |
| void clkmgr_testutils_disable_clock_counts(const dif_clkmgr_t *clkmgr) { | |
| LOG_INFO("Disabling all clock count measurements"); | |
| for (int i = 0; i <= kDifClkmgrMeasureClockUsb; ++i) { | |
| dif_clkmgr_measure_clock_t clock = (dif_clkmgr_measure_clock_t)i; | |
| CHECK_DIF_OK(dif_clkmgr_disable_measure_counts(clkmgr, clock)); | |
| } | |
| } | |
| bool clkmgr_testutils_check_measurement_counts(const dif_clkmgr_t *clkmgr) { | |
| bool success = true; | |
| dif_clkmgr_recov_err_codes_t err_codes; | |
| CHECK_DIF_OK(dif_clkmgr_recov_err_code_get_codes(clkmgr, &err_codes)); | |
| if (err_codes != 0) { | |
| LOG_ERROR("Unexpected recoverable error codes 0x%x", err_codes); | |
| success = false; | |
| } else { | |
| LOG_INFO("Clock measurements are okay"); | |
| } | |
| // Clear recoverable errors. | |
| CHECK_DIF_OK(dif_clkmgr_recov_err_code_clear_codes(clkmgr, ~0u)); | |
| return success; | |
| } | |
| void clkmgr_testutils_enable_external_clock_and_wait_for_completion( | |
| const dif_clkmgr_t *clkmgr, bool is_low_speed) { | |
| LOG_INFO("Configure clkmgr to enable external clock"); | |
| CHECK_DIF_OK(dif_clkmgr_external_clock_set_enabled(clkmgr, is_low_speed)); | |
| CHECK_DIF_OK(dif_clkmgr_wait_for_ext_clk_switch(clkmgr)); | |
| LOG_INFO("Switching to external clock completes"); | |
| } |