Software APIs
clkmgr_testutils.c
1// Copyright lowRISC contributors (OpenTitan project).
2// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3// SPDX-License-Identifier: Apache-2.0
4
5#include "sw/device/lib/testing/clkmgr_testutils.h"
6
9
10#define MODULE_ID MAKE_MODULE_ID('c', 'm', 't')
11
12static_assert(kDtClkmgrCount == 1,
13 "this code assumes that there is a single clkmgr");
14
15enum {
16 // We do not know the exact number of measurable clocks but
17 // there cannot be more than the total number of clocks.
18 kMaxMeasureClockCount = kDtClockCount,
19};
20
21// `extern` declarations to give the inline functions in the
22// corresponding header a link location.
23
24extern bool clkmgr_testutils_get_trans_clock_status(
25 const dif_clkmgr_t *clkmgr, dif_clkmgr_hintable_clock_t clock);
26
27extern status_t clkmgr_testutils_check_trans_clock_gating(
28 const dif_clkmgr_t *clkmgr, dif_clkmgr_hintable_clock_t clock,
29 bool exp_clock_enabled, uint32_t timeout_usec);
30
31// The thresholds are encoded as
32// - max = count + variability
33// - min = count - variability
34typedef struct expected_count_info {
35 uint32_t count;
36 uint32_t variability;
37} expected_count_info_t;
38
39// Map from the DT clock to the clock measurement index.
40// For DT clocks without measurements, this will be set to
41// an invalid value.
42static size_t dt_clk_to_meas_clk[kDtClockCount];
43
44// The expected counts when jitter is disabled.
45static expected_count_info_t kNoJitterCountInfos[kMaxMeasureClockCount];
46
47// The expected counts when jitter is enabled.
48static expected_count_info_t kJitterCountInfos[kMaxMeasureClockCount];
49
50// Notice the expected variability is set to somewhat less than the added
51// variability of the AON and the measured clock.
52
53// The expected variability as a percentage. The AST guarantees 3% for all
54// clocks, including the AON, so the effective variability is set to 5%.
55static uint32_t kVariabilityPercentage = 5;
56
57// The expected variability when jitter is enabled as a percentage. The AST
58// guarantees 10% for jittery clocks and 3% for the AON clock, so the
59// effective variability is set to 12%.
60static uint32_t kJitterVariabilityPercentage = 12;
61
62// Compute the variability for a given number of cycles, adding an extra cycle
63// for synchronizers.
64static inline uint32_t get_count_variability(uint32_t cycles,
65 uint32_t variability_percentage) {
66 return ((cycles * variability_percentage) + 99) / 100 + 1;
67}
68
69static const char *measure_clock_name(const dif_clkmgr_t *clkmgr,
70 dif_clkmgr_measure_clock_t clock) {
71 dt_clkmgr_t dt;
72 dif_result_t res = dif_clkmgr_get_dt(clkmgr, &dt);
73 if (res != kDifOk) {
74 return "<no dt info>";
75 }
76 if (clock >= dt_clkmgr_measurable_clock_count(dt)) {
77 return "<invalid clock>";
78 }
80 // This is not ideal because it relies on top specific names but it's better
81 // than just indices.
82 switch (info.clock) {
83#ifdef OPENTITAN_CLKMGR_HAS_MEAS_CTRL_MAIN
84 case kDtClockMain:
85 return "main_clk";
86#endif
87#ifdef OPENTITAN_CLKMGR_HAS_MEAS_CTRL_IO
88 case kDtClockIo:
89 return "io_clk";
90#endif
91#ifdef OPENTITAN_CLKMGR_HAS_MEAS_CTRL_IO_DIV2
92 case kDtClockIoDiv2:
93 return "io_div2_clk";
94#endif
95#ifdef OPENTITAN_CLKMGR_HAS_MEAS_CTRL_IO_DIV4
96 case kDtClockIoDiv4:
97 return "io_div4_clk";
98#endif
99#ifdef OPENTITAN_CLKMGR_HAS_MEAS_CTRL_USB
100 case kDtClockUsb:
101 return "usb_clk";
102#endif
103 default:
104 return "<unknown>";
105 }
106}
107
108static uint32_t cast_safely(uint64_t val) {
109 CHECK(val <= UINT32_MAX);
110 return (uint32_t)val;
111}
112
113dif_result_t initialize_expected_counts(const dif_clkmgr_t *clkmgr) {
114 dt_clkmgr_t clkmgr_dt;
115 DIF_RETURN_IF_ERROR(dif_clkmgr_get_dt(clkmgr, &clkmgr_dt));
116 // We assume that measurements are made against the clk_aon_i port.
117 dt_clock_t aon_clk_dt = dt_clkmgr_clock(clkmgr_dt, kDtClkmgrClockAon);
118 dt_clock_t main_clk_dt = dt_clkmgr_clock(clkmgr_dt, kDtClkmgrClockMain);
119 uint64_t aon_clk_freq_hz = dt_clock_frequency(aon_clk_dt);
120
121 // Fill the clock map with invalid values.
122 for (dt_clock_t dt_clk = 0; dt_clk < kDtClockCount; dt_clk++) {
123 dt_clk_to_meas_clk[dt_clk] = dt_clkmgr_measurable_clock_count(clkmgr_dt);
124 }
125
126 // Go through all measurable clocks.
127 for (size_t clk_idx = 0;
128 clk_idx < dt_clkmgr_measurable_clock_count(clkmgr_dt); clk_idx++) {
129 dt_clock_t clk_dt = dt_clkmgr_measurable_clock(clkmgr_dt, clk_idx).clock;
130 dt_clk_to_meas_clk[clk_dt] = clk_idx;
131
132 uint64_t clk_freq_hz = dt_clock_frequency(clk_dt);
133 // The expected counts are derived from the ratios of the frequencies of the
134 // various clocks to the AON clock. For example, 48 Mhz / 200 kHz = 240.
135 // Notice the ratios are small enough to fit a uint32_t, even if the Hz
136 // number is in uint64_t.
137 uint32_t clk_count = cast_safely(
138 udiv64_slow(clk_freq_hz, aon_clk_freq_hz, /*rem_out=*/NULL));
139 uint32_t variability =
140 get_count_variability(clk_count, kVariabilityPercentage);
141 LOG_INFO("Variability for %s(%d) %d is %d",
142 measure_clock_name(clkmgr, clk_idx), clk_idx, clk_count,
143 variability);
144
145 // Each clock count is guaranteed by the AST +- 3%. This includes the AON
146 // clock, so we use an effective variability of +- 5%.
147 kNoJitterCountInfos[clk_idx] = (expected_count_info_t){
148 .count = clk_count - 1, .variability = variability};
149
150 // When jitter is enabled only the main clk is affected: the low threshold
151 // should be up to 20% lower, so the expected count is set to 0.9 max, and
152 // the variability is set per kJitterVariabilityPercentage.
153 if (clk_dt != main_clk_dt) {
154 kJitterCountInfos[clk_idx] = kNoJitterCountInfos[clk_idx];
155 } else {
156 kJitterCountInfos[clk_idx] = (expected_count_info_t){
157 .count = clk_count - clk_count / 10,
158 .variability = get_count_variability(clk_count - clk_count / 10,
159 kJitterVariabilityPercentage)};
160 }
161 }
162
163 return kDifOk;
164}
165
166status_t clkmgr_testutils_enable_clock_count(const dif_clkmgr_t *clkmgr,
167 dif_clkmgr_measure_clock_t clock,
168 uint32_t lo_threshold,
169 uint32_t hi_threshold) {
170 LOG_INFO("Enabling clock count measurement for %s(%d) lo %d hi %d",
171 measure_clock_name(clkmgr, clock), clock, lo_threshold,
172 hi_threshold);
173 TRY(dif_clkmgr_enable_measure_counts(clkmgr, clock, lo_threshold,
174 hi_threshold));
175 return OK_STATUS();
176}
177
178status_t clkmgr_testutils_enable_clock_counts_with_expected_thresholds(
179 const dif_clkmgr_t *clkmgr, bool jitter_enabled, bool external_clk,
180 bool low_speed) {
181 static bool counts_initialized = false;
182 if (!counts_initialized) {
183 TRY(initialize_expected_counts(clkmgr));
184 counts_initialized = true;
185 }
186 TRY_CHECK(!(external_clk && jitter_enabled));
187
188 dt_clkmgr_t clkmgr_dt;
189 TRY(dif_clkmgr_get_dt(clkmgr, &clkmgr_dt));
190
191 for (size_t clk = 0; clk < dt_clkmgr_measurable_clock_count(clkmgr_dt);
192 ++clk) {
193 const expected_count_info_t *count_info;
194 if (jitter_enabled) {
195 count_info = &kJitterCountInfos[clk];
196 } else if (external_clk) {
197#if defined(OPENTITAN_IS_EARLGREY)
198 dt_clock_t dt_actual_clk =
199 dt_clkmgr_measurable_clock(clkmgr_dt, clk).clock;
200 // When software switches to an external clock, all clock sources switch
201 // to an external source. In particular, the main clock becomes the same
202 // as the IO clock.
203 if (dt_actual_clk == kDtClockMain) {
204 dt_actual_clk = kDtClockIo;
205 }
206 // If software requests a low speed external clock, internal dividers are
207 // stepped down so that divide-by-2 and divide-by-4 remain at their
208 // nominal frequencies. On the other hand, the divide-by-1 (i.e. the IO
209 // clock) becomes equal to divide-by-2.
210 if (low_speed && dt_actual_clk == kDtClockIo) {
211 dt_actual_clk = kDtClockIoDiv2;
212 }
213 CHECK(dt_clk_to_meas_clk[dt_actual_clk] <
215 "this clock is not measurable!");
216 count_info = &kNoJitterCountInfos[dt_clk_to_meas_clk[dt_actual_clk]];
217#elif defined(OPENTITAN_IS_DARJEELING)
218 TRY_CHECK(false, "Darjeeling has no external clock");
220#else
221#error Unsupported top
222#endif
223 } else {
224 count_info = &kNoJitterCountInfos[clk];
225 }
226 TRY(clkmgr_testutils_enable_clock_count(
227 clkmgr, (dif_clkmgr_measure_clock_t)clk,
228 count_info->count - count_info->variability,
229 count_info->count + count_info->variability));
230 }
231 return OK_STATUS();
232}
233
234status_t clkmgr_testutils_check_measurement_enables(
235 const dif_clkmgr_t *clkmgr, dif_toggle_t expected_status) {
236 bool success = true;
237 for (int i = 0; i < kDifClkmgrMeasureClockCount; ++i) {
238 dif_clkmgr_measure_clock_t clock = (dif_clkmgr_measure_clock_t)i;
239 dif_toggle_t actual_status;
240 TRY(dif_clkmgr_measure_counts_get_enable(clkmgr, clock, &actual_status));
241 if (actual_status != expected_status) {
242 LOG_INFO("Unexpected enable for clock %d: expected %s", i,
243 (expected_status == kDifToggleEnabled ? "enabled" : "disabled"));
244 success = false;
245 }
246 }
247 return OK_STATUS(success);
248}
249
250status_t clkmgr_testutils_disable_clock_counts(const dif_clkmgr_t *clkmgr) {
251 LOG_INFO("Disabling all clock count measurements");
252 for (int i = 0; i < kDifClkmgrMeasureClockCount; ++i) {
253 dif_clkmgr_measure_clock_t clock = (dif_clkmgr_measure_clock_t)i;
254 TRY(dif_clkmgr_disable_measure_counts(clkmgr, clock));
255 }
256 LOG_INFO("Disabling all clock count done");
257 return OK_STATUS();
258}
259
260status_t clkmgr_testutils_check_measurement_counts(const dif_clkmgr_t *clkmgr) {
261 status_t result = OK_STATUS();
263 TRY(dif_clkmgr_recov_err_code_get_codes(clkmgr, &err_codes));
264 if (err_codes != 0) {
265 LOG_ERROR("Unexpected recoverable error codes 0x%x", err_codes);
266 result = INTERNAL();
267 } else {
268 LOG_INFO("Clock measurements are okay");
269 }
270 // Clear recoverable errors.
271 TRY(dif_clkmgr_recov_err_code_clear_codes(clkmgr, ~0u));
272 return result;
273}
274
275status_t clkmgr_testutils_enable_external_clock_blocking(
276 const dif_clkmgr_t *clkmgr, bool is_low_speed) {
277 LOG_INFO("Configure clkmgr to enable external clock");
278 TRY(dif_clkmgr_external_clock_set_enabled(clkmgr, is_low_speed));
279 TRY(dif_clkmgr_wait_for_ext_clk_switch(clkmgr));
280 LOG_INFO("Switching to external clock completes");
281 return OK_STATUS();
282}