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