1 /*
2  * Arm SCP/MCP Software
3  * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved.
4  *
5  * SPDX-License-Identifier: BSD-3-Clause
6  *
7  * Unit test for mod_perf_controller.c
8  */
9 
10 #include "scp_unity.h"
11 #include "unity.h"
12 
13 #include <Mockmod_perf_controller_extra.h>
14 #include <config_mod_perf_controller.h>
15 #include <internal/perf_controller.h>
16 
17 #include <mod_perf_controller.h>
18 
19 #include <fwk_module_idx.h>
20 
21 #include <stdlib.h>
22 
23 #include UNIT_TEST_SRC
24 
25 static struct mod_perf_controller_cluster_ctx
26     test_cluster_ctx_table[TEST_CLUSTER_COUNT];
27 static struct mod_perf_controller_core_ctx
28     test_core_ctx_table[TEST_CLUSTER_COUNT][MAX_CORE_PER_CLUSTER];
29 
30 static struct mod_perf_controller_drv_api perf_driver_api = {
31     .set_performance_level = driver_set_performance_level,
32 };
33 
34 static struct mod_perf_controller_power_model_api power_model_api = {
35     .power_to_performance = power_to_performance,
36 };
37 
setUp(void)38 void setUp(void)
39 {
40     unsigned int cluster_idx;
41     struct mod_perf_controller_cluster_ctx *cluster_ctx;
42 
43     perf_controller_ctx.cluster_ctx_table = test_cluster_ctx_table;
44     perf_controller_ctx.cluster_count = TEST_CLUSTER_COUNT;
45 
46     for (cluster_idx = 0U; cluster_idx < TEST_CLUSTER_COUNT; cluster_idx++) {
47         cluster_ctx = &perf_controller_ctx.cluster_ctx_table[cluster_idx];
48         cluster_ctx->core_ctx_table = test_core_ctx_table[cluster_idx];
49         cluster_ctx->perf_driver_api = &perf_driver_api;
50         cluster_ctx->power_model_api = &power_model_api;
51         cluster_ctx->config = (struct mod_perf_controller_cluster_config *)
52                                   cluster_config[cluster_idx]
53                                       .data;
54         cluster_ctx->core_count = cluster_config[cluster_idx].sub_element_count;
55     }
56 
57     internal_api.get_cores_min_power_limit = get_cores_min_power_limit_stub;
58     internal_api.cluster_apply_performance_granted =
59         cluster_apply_performance_granted_stub;
60 }
61 
tearDown(void)62 void tearDown(void)
63 {
64     Mockmod_perf_controller_extra_Verify();
65     Mockmod_perf_controller_extra_Destroy();
66 }
67 
68 /*!
69  * \brief Helper function to compare two values.
70  *
71  * \details returns -1 when a < b,
72  *          returns 1 when a >b,
73  *          return 0 when a = b.
74  */
helper_comp(const void * a,const void * b)75 int helper_comp(const void *a, const void *b)
76 {
77     return (*(int *)a > *(int *)b) - (*(int *)a < *(int *)b);
78 }
79 
test_set_performance_level_within_limits(void)80 void test_set_performance_level_within_limits(void)
81 {
82     int status;
83     unsigned int cluster_idx;
84     fwk_id_t cluster_id;
85     struct mod_perf_controller_cluster_ctx *cluster_ctx;
86     uintptr_t cookie;
87     uint32_t performance_level;
88 
89     for (cluster_idx = 0U; cluster_idx < TEST_CLUSTER_COUNT; cluster_idx++) {
90         cluster_id =
91             FWK_ID_ELEMENT(FWK_MODULE_IDX_PERF_CONTROLLER, cluster_idx);
92         cluster_ctx = &perf_controller_ctx.cluster_ctx_table[cluster_idx];
93 
94         performance_level = 10U;
95         cluster_ctx->performance_limit = performance_level;
96 
97         cookie = 15U;
98         driver_set_performance_level_ExpectAndReturn(
99             cluster_ctx->config->performance_driver_id,
100             cookie,
101             performance_level,
102             FWK_SUCCESS);
103 
104         status = mod_perf_controller_set_performance_level(
105             cluster_id, cookie, performance_level);
106 
107         TEST_ASSERT_EQUAL(
108             cluster_ctx->performance_request_details.level, performance_level);
109         TEST_ASSERT_EQUAL(cluster_ctx->performance_request_details.cookie, 0U);
110         TEST_ASSERT_EQUAL(status, FWK_SUCCESS);
111     }
112 }
113 
test_set_performance_level_out_of_limits(void)114 void test_set_performance_level_out_of_limits(void)
115 {
116     int status;
117     unsigned int cluster_idx;
118     fwk_id_t cluster_id;
119     struct mod_perf_controller_cluster_ctx *cluster_ctx;
120     uintptr_t cookie;
121     uint32_t performance_level;
122 
123     for (cluster_idx = 0U; cluster_idx < TEST_CLUSTER_COUNT; cluster_idx++) {
124         cluster_id =
125             FWK_ID_ELEMENT(FWK_MODULE_IDX_PERF_CONTROLLER, cluster_idx);
126         cluster_ctx = &perf_controller_ctx.cluster_ctx_table[cluster_idx];
127 
128         cluster_ctx->performance_limit = 10U;
129         performance_level = cluster_ctx->performance_limit + 1U;
130 
131         cookie = 1U;
132         status = mod_perf_controller_set_performance_level(
133             cluster_id, cookie, performance_level);
134 
135         TEST_ASSERT_EQUAL(
136             cluster_ctx->performance_request_details.level, performance_level);
137         TEST_ASSERT_EQUAL(
138             cluster_ctx->performance_request_details.cookie, cookie);
139         TEST_ASSERT_EQUAL(status, FWK_PENDING);
140     }
141 }
142 
test_set_limit_success(void)143 void test_set_limit_success(void)
144 {
145     int status;
146     fwk_id_t core_id;
147     struct mod_perf_controller_core_ctx *core_ctx;
148     struct mod_perf_controller_cluster_ctx *cluster_ctx;
149     unsigned int cluster_idx = TEST_CLUSTER_COUNT - 1U;
150     unsigned int core_idx = MAX_CORE_PER_CLUSTER - 1U;
151     uint32_t power_limit;
152 
153     for (cluster_idx = 0U; cluster_idx < TEST_CLUSTER_COUNT - 1U;
154          cluster_idx++) {
155         cluster_ctx = &perf_controller_ctx.cluster_ctx_table[cluster_idx];
156         for (core_idx = 0U; core_idx < cluster_ctx->core_count; core_idx++) {
157             core_id = FWK_ID_SUB_ELEMENT(
158                 FWK_MODULE_IDX_PERF_CONTROLLER, cluster_idx, core_idx);
159             core_ctx = &perf_controller_ctx.cluster_ctx_table[cluster_idx]
160                             .core_ctx_table[core_idx];
161 
162             power_limit = 20U;
163             status = mod_perf_controller_set_limit(core_id, power_limit);
164 
165             TEST_ASSERT_EQUAL(core_ctx->power_limit, power_limit);
166             TEST_ASSERT_EQUAL(status, FWK_SUCCESS);
167         }
168     }
169 }
170 
test_get_cores_min_power_limit(void)171 void test_get_cores_min_power_limit(void)
172 {
173     uint32_t min_power_limit;
174     unsigned int core_idx;
175     unsigned int cluster_idx;
176     struct mod_perf_controller_cluster_ctx *cluster_ctx;
177     uint32_t core_power_limit_test_values[MAX_CORE_PER_CLUSTER] = {
178         100U, 300U, 200U, 10U
179     };
180 
181     for (cluster_idx = 0U; cluster_idx < TEST_CLUSTER_COUNT - 1U;
182          cluster_idx++) {
183         cluster_ctx = &perf_controller_ctx.cluster_ctx_table[cluster_idx];
184 
185         for (core_idx = 0u; core_idx < cluster_ctx->core_count; core_idx++) {
186             cluster_ctx->core_ctx_table[core_idx].power_limit =
187                 core_power_limit_test_values[core_idx];
188         }
189 
190         min_power_limit = get_cores_min_power_limit(cluster_ctx);
191 
192         /*
193             Using sorting to determine the minimum. qsort is used as it is a
194             standard function that would make the test easier. The heavy lifting
195             still needs to be done on the implementation side.
196         */
197 
198         qsort(
199             core_power_limit_test_values,
200             cluster_ctx->core_count,
201             sizeof(core_power_limit_test_values[0]),
202             helper_comp);
203 
204         TEST_ASSERT_EQUAL(core_power_limit_test_values[0], min_power_limit);
205     }
206 }
207 
test_controller_apply_performance_granted_within_limits(void)208 void test_controller_apply_performance_granted_within_limits(void)
209 {
210     int status;
211     unsigned int cluster_idx;
212     struct mod_perf_controller_cluster_ctx *cluster_ctx;
213     uint32_t min_power_limit;
214     uint32_t performance_limit;
215     uint32_t *requested_performance;
216     uintptr_t *cookie;
217 
218     for (cluster_idx = 0U; cluster_idx < TEST_CLUSTER_COUNT - 1U;
219          cluster_idx++) {
220         cluster_ctx = &perf_controller_ctx.cluster_ctx_table[cluster_idx];
221         requested_performance = &cluster_ctx->performance_request_details.level;
222         cookie = &cluster_ctx->performance_request_details.cookie;
223 
224         min_power_limit = 500U;
225         performance_limit = 700U;
226 
227         get_cores_min_power_limit_stub_ExpectAndReturn(
228             cluster_ctx, min_power_limit);
229 
230         power_to_performance_ExpectAndReturn(
231             cluster_ctx->config->power_model_id,
232             min_power_limit,
233             NULL,
234             FWK_SUCCESS);
235 
236         power_to_performance_IgnoreArg_performance_level();
237 
238         power_to_performance_ReturnThruPtr_performance_level(
239             &performance_limit);
240 
241         *requested_performance = performance_limit;
242         *cookie = 2U;
243 
244         driver_set_performance_level_ExpectAndReturn(
245             cluster_ctx->config->performance_driver_id,
246             *cookie,
247             *requested_performance,
248             FWK_SUCCESS);
249 
250         status = cluster_apply_performance_granted(cluster_ctx);
251 
252         TEST_ASSERT_EQUAL(status, FWK_SUCCESS);
253     }
254 }
255 
test_controller_apply_performance_granted_out_of_limits(void)256 void test_controller_apply_performance_granted_out_of_limits(void)
257 {
258     int status;
259     unsigned int cluster_idx;
260     struct mod_perf_controller_cluster_ctx *cluster_ctx;
261     uint32_t min_power_limit;
262     uint32_t performance_limit;
263     uint32_t *requested_performance;
264     uintptr_t *cookie;
265 
266     for (cluster_idx = 0U; cluster_idx < TEST_CLUSTER_COUNT - 1U;
267          cluster_idx++) {
268         cluster_ctx = &perf_controller_ctx.cluster_ctx_table[cluster_idx];
269         requested_performance = &cluster_ctx->performance_request_details.level;
270         cookie = &cluster_ctx->performance_request_details.cookie;
271 
272         min_power_limit = 800U;
273         performance_limit = 991U;
274 
275         get_cores_min_power_limit_stub_ExpectAndReturn(
276             cluster_ctx, min_power_limit);
277 
278         power_to_performance_ExpectAndReturn(
279             cluster_ctx->config->power_model_id,
280             min_power_limit,
281             NULL,
282             FWK_SUCCESS);
283 
284         power_to_performance_IgnoreArg_performance_level();
285 
286         power_to_performance_ReturnThruPtr_performance_level(
287             &performance_limit);
288 
289         *requested_performance = performance_limit + 1U;
290         *cookie = 3U;
291 
292         driver_set_performance_level_ExpectAndReturn(
293             cluster_ctx->config->performance_driver_id,
294             0U, /* No cookie */
295             performance_limit,
296             FWK_SUCCESS);
297 
298         status = cluster_apply_performance_granted(cluster_ctx);
299 
300         TEST_ASSERT_EQUAL(status, FWK_SUCCESS);
301     }
302 }
303 
test_controller_apply_performance_granted_success(void)304 void test_controller_apply_performance_granted_success(void)
305 {
306     int status;
307     unsigned int cluster_idx;
308     struct mod_perf_controller_cluster_ctx *cluster_ctx;
309 
310     for (cluster_idx = 0U; cluster_idx < TEST_CLUSTER_COUNT; cluster_idx++) {
311         cluster_ctx = &perf_controller_ctx.cluster_ctx_table[cluster_idx];
312         cluster_apply_performance_granted_stub_ExpectAndReturn(
313             cluster_ctx, FWK_SUCCESS);
314     }
315 
316     status = mod_perf_controller_apply_performance_granted();
317 
318     TEST_ASSERT_EQUAL(status, FWK_SUCCESS);
319 }
320 
perf_controller_test_main(void)321 int perf_controller_test_main(void)
322 {
323     UNITY_BEGIN();
324 
325     RUN_TEST(test_set_performance_level_within_limits);
326     RUN_TEST(test_set_performance_level_out_of_limits);
327     RUN_TEST(test_set_limit_success);
328     RUN_TEST(test_get_cores_min_power_limit);
329     RUN_TEST(test_controller_apply_performance_granted_within_limits);
330     RUN_TEST(test_controller_apply_performance_granted_out_of_limits);
331     RUN_TEST(test_controller_apply_performance_granted_success);
332 
333     return UNITY_END();
334 }
335 
336 #if !defined(TEST_ON_TARGET)
main(void)337 int main(void)
338 {
339     return perf_controller_test_main();
340 }
341 #endif
342