1 /*
2  * Copyright (c) 2006-2025, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2025-07-03     rcitach      test case for irq latency
9  */
10 
11 #include <rtthread.h>
12 #include <rtdevice.h>
13 #include <rtservice.h>
14 #include <utest.h>
15 #include <utest_assert.h>
16 #include <perf_tc.h>
17 
18 #define RET_INT             0
19 #define RET_DECIMALS        1
20 
21 #define GET_INT(num)        split_double(num, RET_INT)
22 #define GET_DECIMALS(num)   split_double(num, RET_DECIMALS)
23 
24 static rt_device_t hw_dev = RT_NULL;
25 static rt_hwtimerval_t timeout_s = {0};
26 
27 typedef rt_err_t (*testcase_function)(rt_perf_t *perf);
28 testcase_function test_func_ptrs[] =
29 {
30     context_switch_test,
31     rt_perf_thread_sem,
32     rt_perf_thread_event,
33     rt_perf_thread_mq,
34     rt_perf_thread_mbox,
35     rt_perf_irq_latency,    /* Timer Interrupt Source */
36     RT_NULL
37 };
38 
rt_perf_get_timer_us(void)39 static rt_uint32_t rt_perf_get_timer_us(void)
40 {
41     rt_hwtimerval_t timer_val = {0};
42     if (hw_dev && rt_device_read(hw_dev, 0, &timer_val, sizeof(rt_hwtimerval_t)))
43     {
44         return (rt_uint32_t)(timer_val.sec * 1000000u + timer_val.usec); /* return us */
45     }
46     return 0;
47 }
48 
rt_perf_start_impl(rt_perf_t * perf,rt_hwtimerval_t * timeout)49 void rt_perf_start_impl(rt_perf_t *perf, rt_hwtimerval_t *timeout)
50 {
51     if (hw_dev)
52     {
53         if (timeout == RT_NULL)
54             timeout = &timeout_s;
55         rt_device_write(hw_dev, 0, timeout, sizeof(rt_hwtimerval_t));
56     }
57     perf->begin_time = rt_perf_get_timer_us();
58 }
59 
rt_perf_stop(rt_perf_t * perf)60 void rt_perf_stop(rt_perf_t *perf)
61 {
62     perf->real_time = rt_perf_get_timer_us() - perf->begin_time;
63 
64     if(perf->local_modify) perf->local_modify(perf);
65     if (perf->real_time > perf->max_time)
66     {
67         perf->max_time = perf->real_time;
68     }
69 
70     if (perf->real_time < perf->min_time)
71     {
72         perf->min_time = perf->real_time;
73     }
74 
75     perf->count++;
76     perf->tot_time += perf->real_time;
77 
78     if(hw_dev)
79         rt_device_control(hw_dev, HWTIMER_CTRL_STOP, NULL);
80 }
81 
split_double(double num,rt_uint32_t type)82 static rt_int32_t split_double(double num, rt_uint32_t type)
83 {
84     if (type == RET_INT)
85     {
86         return (rt_int32_t)num;
87     }
88     else if (type == RET_DECIMALS)
89     {
90         return (rt_int32_t)((num - (rt_int32_t)num) * 10000);
91     }
92     else
93     {
94         return (-1);
95     }
96 
97     return (-1);
98 }
99 
rt_perf_dump(rt_perf_t * perf)100 void rt_perf_dump( rt_perf_t *perf)
101 {
102     static rt_uint32_t test_index = 1;
103     char avg_str[10] = {0};
104     if(perf->dump_head)
105     {
106         rt_kprintf("Test No | Test Name            | Count | Total Time (us) | Max Time (us) | Min Time (us) | Avg Time (us)\n");
107         rt_kprintf("--------|----------------------|-------|-----------------|---------------|---------------|--------------\n");
108         perf->dump_head = RT_FALSE;
109     }
110 
111     if (perf->count)
112         perf->avg_time = (double)perf->tot_time / perf->count;
113     else
114         perf->avg_time = 0.0;
115 
116     rt_sprintf(avg_str, "%u.%04u", GET_INT(perf->avg_time), GET_DECIMALS(perf->avg_time));
117 
118     rt_kprintf("%7u | %-20s | %5u | %15u | %13u | %13u | %12s\n",
119                 test_index++,
120                 perf->name,
121                 perf->count,
122                 perf->tot_time,
123                 perf->max_time,
124                 perf->min_time,
125                 avg_str);
126 }
127 
rt_perf_clear(rt_perf_t * perf)128 static void rt_perf_clear(rt_perf_t *perf)
129 {
130     perf->local_modify = NULL;
131     perf->begin_time = 0;
132     perf->real_time = 0;
133     perf->tot_time = 0;
134     perf->max_time = 0;
135     perf->min_time = RT_UINT32_MAX;
136     perf->count = 0;
137     perf->avg_time = 0;
138     perf->tmp_time = 0;
139 }
140 
rt_perf_all_test(void)141 static void rt_perf_all_test(void)
142 {
143 
144     rt_perf_t *perf_data = rt_malloc(sizeof(rt_perf_t));
145     if (perf_data == RT_NULL)
146     {
147         return;
148     }
149     perf_data->lock = rt_mutex_create("perf", RT_IPC_FLAG_PRIO);
150     perf_data->dump_head = RT_TRUE;
151     rt_kprintf("\n === Performance Test Results Start ===\n");
152     for (int i = 0; test_func_ptrs[i] != RT_NULL; i++)
153     {
154         rt_perf_clear(perf_data);
155         if (test_func_ptrs[i](perf_data) != RT_EOK)
156         {
157             LOG_E("%s test fail",perf_data->name);
158             continue;
159         }
160     }
161     rt_kprintf("\n === Performance Test Results End ===\n");
162     rt_mutex_delete(perf_data->lock);
163     rt_free(perf_data);
164 }
165 
utest_tc_init(void)166 static rt_err_t utest_tc_init(void)
167 {
168     int ret = RT_EOK;
169 
170     hw_dev = rt_device_find(UTEST_HWTIMER_DEV_NAME);
171     if (hw_dev == RT_NULL)
172     {
173         ret = RT_ERROR;
174         LOG_E("hwtimer sample run failed! can't find %s device!", UTEST_HWTIMER_DEV_NAME);
175         return ret;
176     }
177     ret = rt_device_open(hw_dev, RT_DEVICE_OFLAG_RDWR);
178     if (ret != RT_EOK)
179     {
180         LOG_E("open %s device failed!", UTEST_HWTIMER_DEV_NAME);
181         return ret;
182     }
183 
184     timeout_s.sec  = 10;      /* No modification is necessary here, use the fixed value */
185     timeout_s.usec = 0;
186 
187     return ret;
188 }
189 
utest_tc_cleanup(void)190 static rt_err_t utest_tc_cleanup(void)
191 {
192     if(hw_dev) rt_device_close(hw_dev);
193     return RT_EOK;
194 }
195 
testcase(void)196 static void testcase(void)
197 {
198     UTEST_UNIT_RUN(rt_perf_all_test);
199 }
200 
201 UTEST_TC_EXPORT(testcase, "testcase.pref.all", utest_tc_init, utest_tc_cleanup, 10);
202 
203