| /* | |
| * Copyright 2023 Google LLC | |
| * Copyright lowRISC contributors | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| #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[kDifClkmgrMeasureClockVideo + 1] = { | |
| "audio_clk", "io_clk", "io_div2_clk", "io_div4_clk", "main_clk", "ml_clk", | |
| "smc_clk", "usb_clk", "video_clk"}; | |
| // Clocks defined in Matcha | |
| const uint64_t kClockFreqSmcHz = 96 * 1000 * 1000; // 96MHz | |
| const uint64_t kClockFreqMlHz = 96 * 1000 * 1000; // 96MHz | |
| const uint64_t kClockFreqVideoHz = 96 * 1000 * 1000; // 100MHz | |
| const uint64_t kClockFreqAudioHz = 48 * 1000 * 1000; // 48MHz | |
| // `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[kDifClkmgrMeasureClockVideo + 1]; | |
| // The expected counts when jitter is enabled. | |
| static expected_count_info_t kJitterCountInfos[kDifClkmgrMeasureClockVideo + 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)); | |
| const uint32_t kDeviceSmcCount = | |
| cast_safely(udiv64_slow(kClockFreqSmcHz, kClockFreqAonHz, | |
| /*rem_out=*/NULL)); | |
| const uint32_t kDeviceMlCount = | |
| cast_safely(udiv64_slow(kClockFreqMlHz, kClockFreqAonHz, | |
| /*rem_out=*/NULL)); | |
| const uint32_t kDeviceVideoCount = | |
| cast_safely(udiv64_slow(kClockFreqVideoHz, kClockFreqAonHz, | |
| /*rem_out=*/NULL)); | |
| const uint32_t kDeviceAudioCount = | |
| cast_safely(udiv64_slow(kClockFreqAudioHz, 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}; | |
| kNoJitterCountInfos[kDifClkmgrMeasureClockSmc] = | |
| (expected_count_info_t){.count = kDeviceSmcCount - 1, .variability = 1}; | |
| kNoJitterCountInfos[kDifClkmgrMeasureClockMl] = | |
| (expected_count_info_t){.count = kDeviceMlCount - 1, .variability = 1}; | |
| kNoJitterCountInfos[kDifClkmgrMeasureClockVideo] = | |
| (expected_count_info_t){.count = kDeviceVideoCount - 1, .variability = 1}; | |
| kNoJitterCountInfos[kDifClkmgrMeasureClockAudio] = | |
| (expected_count_info_t){.count = kDeviceAudioCount - 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}; | |
| kJitterCountInfos[kDifClkmgrMeasureClockSmc] = | |
| (expected_count_info_t){.count = kDeviceSmcCount - kDeviceSmcCount / 10, | |
| .variability = kDeviceSmcCount / 10}; | |
| kJitterCountInfos[kDifClkmgrMeasureClockMl] = | |
| (expected_count_info_t){.count = kDeviceMlCount - kDeviceMlCount / 10, | |
| .variability = kDeviceMlCount / 10}; | |
| kJitterCountInfos[kDifClkmgrMeasureClockVideo] = | |
| (expected_count_info_t){.count = kDeviceVideoCount - kDeviceVideoCount / 10, | |
| .variability = kDeviceVideoCount / 10}; | |
| kJitterCountInfos[kDifClkmgrMeasureClockAudio] = | |
| (expected_count_info_t){.count = kDeviceAudioCount - kDeviceAudioCount / 10, | |
| .variability = kDeviceAudioCount / 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"; | |
| kDifClkmgrMeasureClockSmc: | |
| return "smc"; | |
| kDifClkmgrMeasureClockMl: | |
| return "ml"; | |
| kDifClkmgrMeasureClockVideo: | |
| return "video"; | |
| kDifClkmgrMeasureClockAudio: | |
| return "audio"; | |
| 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 || | |
| clk == kDifClkmgrMeasureClockAudio || | |
| clk == kDifClkmgrMeasureClockSmc || | |
| clk == kDifClkmgrMeasureClockMl || | |
| clk == kDifClkmgrMeasureClockVideo) { | |
| count_info = &kNoJitterCountInfos[kDifClkmgrMeasureClockIoDiv2]; | |
| } else { | |
| count_info = &kNoJitterCountInfos[clk]; | |
| } | |
| } else { | |
| if (clk == kDifClkmgrMeasureClockMain || | |
| clk == kDifClkmgrMeasureClockAudio || | |
| clk == kDifClkmgrMeasureClockSmc || | |
| clk == kDifClkmgrMeasureClockMl || | |
| clk == kDifClkmgrMeasureClockVideo) { | |
| 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 = 0; i <= kDifClkmgrMeasureClockVideo; ++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 <= kDifClkmgrMeasureClockVideo; ++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"); | |
| } |