| // 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"); |
| } |