blob: 35ad6af99eee35ebc780cca4a24f9e28b9dcb391 [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/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 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 479 and variability to 1, meaning the max threshold is 480,
// and the min to 478. Notice the target counts could be computed from the
// device frequencies, but they are expressed as uint64_t, and are not
// compile-time constants.
static const expected_count_info_t kNoJitterCountInfos[] = {
{.count = 479, .variability = 1}, {.count = 239, .variability = 1},
{.count = 119, .variability = 1}, {.count = 499, .variability = 1},
{.count = 239, .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.
static const expected_count_info_t kJitterCountInfos[] = {
{.count = 480 - 48, .variability = 48},
{.count = 240 - 24, .variability = 24},
{.count = 120 - 12, .variability = 12},
{.count = 500 - 50, .variability = 50},
{.count = 240 - 24, .variability = 24},
};
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 %0d hi %0d",
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) {
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);
}
}
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));
}
}
void clkmgr_testutils_check_measurement_counts(const dif_clkmgr_t *clkmgr) {
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);
} else {
LOG_INFO("Clock measurements are okay");
}
// clear recoverable errors
CHECK_DIF_OK(dif_clkmgr_recov_err_code_clear_codes(clkmgr, ~0u));
}
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");
}