blob: 5df28809bca5f331760e11871f606076b9ec196c [file] [log] [blame]
/*
* 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");
}