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