1 /*
2 * Copyright (c) 2006-2024 RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2006-03-12 Bernard first version
9 * 2006-05-27 Bernard add support for same priority thread schedule
10 * 2006-08-10 Bernard remove the last rt_schedule in rt_tick_increase
11 * 2010-03-08 Bernard remove rt_passed_second
12 * 2010-05-20 Bernard fix the tick exceeds the maximum limits
13 * 2010-07-13 Bernard fix rt_tick_from_millisecond issue found by kuronca
14 * 2011-06-26 Bernard add rt_tick_set function.
15 * 2018-11-22 Jesven add per cpu tick
16 * 2020-12-29 Meco Man implement rt_tick_get_millisecond()
17 * 2021-06-01 Meco Man add critical section projection for rt_tick_increase()
18 * 2023-09-15 xqyjlj perf rt_hw_interrupt_disable/enable
19 * 2023-10-16 RiceChen fix: only the main core detection rt_timer_check(), in SMP mode
20 */
21
22 #include <rthw.h>
23 #include <rtthread.h>
24 #include <rtatomic.h>
25
26 #if defined(RT_USING_SMART) && defined(RT_USING_VDSO)
27 #include <vdso.h>
28 #endif
29
30 #ifdef RT_USING_SMP
31 #define rt_tick rt_cpu_index(0)->tick
32 #else
33 static volatile rt_atomic_t rt_tick = 0;
34 #endif /* RT_USING_SMP */
35
36 #if defined(RT_USING_HOOK) && defined(RT_HOOK_USING_FUNC_PTR)
37 static void (*rt_tick_hook)(void);
38
39 /**
40 * @addtogroup group_hook
41 */
42
43 /**@{*/
44
45 /**
46 * @brief This function will set a hook function, which will be invoked when tick increase
47 *
48 *
49 * @param hook the hook function
50 */
rt_tick_sethook(void (* hook)(void))51 void rt_tick_sethook(void (*hook)(void))
52 {
53 rt_tick_hook = hook;
54 }
55 /**@}*/
56 #endif /* RT_USING_HOOK */
57
58 /**
59 * @addtogroup group_clock_management
60 */
61
62 /**@{*/
63
64 /**
65 * @brief This function will return current tick from operating system startup.
66 *
67 * @return Return current tick.
68 */
rt_tick_get(void)69 rt_tick_t rt_tick_get(void)
70 {
71 /* return the global tick */
72 return (rt_tick_t)rt_atomic_load(&(rt_tick));
73 }
74 RTM_EXPORT(rt_tick_get);
75
76 /**
77 * @brief This function will return delta tick from base.
78 *
79 * @param base to consider
80 *
81 * @return Return delta tick.
82 */
rt_tick_get_delta(rt_tick_t base)83 rt_tick_t rt_tick_get_delta(rt_tick_t base)
84 {
85 rt_tick_t tnow = rt_tick_get();
86 if (tnow >= base)
87 return tnow - base;
88 return RT_TICK_MAX - base + tnow + 1;
89 }
90 RTM_EXPORT(rt_tick_get_delta);
91
92 /**
93 * @brief This function will set current tick.
94 *
95 * @param tick is the value that you will set.
96 */
rt_tick_set(rt_tick_t tick)97 void rt_tick_set(rt_tick_t tick)
98 {
99 rt_atomic_store(&(rt_tick), tick);
100 }
101
102 #ifdef RT_USING_CPU_USAGE_TRACER
_update_process_times(rt_tick_t tick)103 static void _update_process_times(rt_tick_t tick)
104 {
105 struct rt_thread *thread = rt_thread_self();
106 struct rt_cpu *pcpu = rt_cpu_self();
107
108 if (!LWP_IS_USER_MODE(thread))
109 {
110 thread->user_time += tick;
111 pcpu->cpu_stat.user += tick;
112 }
113 else
114 {
115 thread->system_time += tick;
116 if (thread == pcpu->idle_thread)
117 {
118 pcpu->cpu_stat.idle += tick;
119 }
120 else
121 {
122 pcpu->cpu_stat.system += tick;
123 }
124 }
125 }
126
127 #else
128
129 #define _update_process_times(tick)
130 #endif /* RT_USING_CPU_USAGE_TRACER */
131
132 /**
133 * @brief This function will notify kernel there is one tick passed.
134 * Normally, this function is invoked by clock ISR.
135 */
rt_tick_increase(void)136 void rt_tick_increase(void)
137 {
138 RT_ASSERT(rt_interrupt_get_nest() > 0);
139
140 RT_OBJECT_HOOK_CALL(rt_tick_hook, ());
141
142 /* tracing cpu usage */
143 _update_process_times(1);
144
145 /* increase the global tick */
146 #ifdef RT_USING_SMP
147 /* get percpu and increase the tick */
148 rt_atomic_add(&(rt_cpu_self()->tick), 1);
149 #else
150 rt_atomic_add(&(rt_tick), 1);
151 #endif /* RT_USING_SMP */
152
153 /* check time slice */
154 rt_sched_tick_increase(1);
155
156 /* check timer */
157 #ifdef RT_USING_SMP
158 if (rt_cpu_get_id() != 0)
159 {
160 return;
161 }
162 #endif
163 rt_timer_check();
164 }
165
166 /**
167 * @brief This function will notify kernel there is n tick passed.
168 * Normally, this function is invoked by clock ISR.
169 */
rt_tick_increase_tick(rt_tick_t tick)170 void rt_tick_increase_tick(rt_tick_t tick)
171 {
172 RT_ASSERT(rt_interrupt_get_nest() > 0);
173
174 RT_OBJECT_HOOK_CALL(rt_tick_hook, ());
175
176 /* tracing cpu usage */
177 _update_process_times(tick);
178
179 /* increase the global tick */
180 #ifdef RT_USING_SMP
181 /* get percpu and increase the tick */
182 rt_atomic_add(&(rt_cpu_self()->tick), tick);
183 #else
184 rt_atomic_add(&(rt_tick), tick);
185 #endif /* RT_USING_SMP */
186
187 /* check time slice */
188 rt_sched_tick_increase(tick);
189
190 /* check timer */
191 #ifdef RT_USING_SMP
192 if (rt_cpu_get_id() != 0)
193 {
194 return;
195 }
196 #endif
197 rt_timer_check();
198
199 #ifdef RT_USING_VDSO
200 rt_vdso_update_glob_time();
201 #endif
202 }
203
204 /**
205 * @brief This function will calculate the tick from millisecond.
206 *
207 * @param ms is the specified millisecond.
208 * - Negative Number wait forever
209 * - Zero not wait
210 * - Max 0x7fffffff
211 *
212 * @return Return the calculated tick.
213 */
rt_tick_from_millisecond(rt_int32_t ms)214 rt_tick_t rt_tick_from_millisecond(rt_int32_t ms)
215 {
216 rt_tick_t tick;
217
218 if (ms < 0)
219 {
220 tick = (rt_tick_t)RT_WAITING_FOREVER;
221 }
222 else
223 {
224 #if RT_TICK_PER_SECOND == 1000u
225 tick = ms;
226 #else
227 tick = RT_TICK_PER_SECOND * (ms / 1000);
228 tick += (RT_TICK_PER_SECOND * (ms % 1000) + 999) / 1000;
229 #endif /* RT_TICK_PER_SECOND == 1000u */
230 }
231
232 /* return the calculated tick */
233 return tick;
234 }
235 RTM_EXPORT(rt_tick_from_millisecond);
236
237 /**
238 * @brief This function will return the passed millisecond from boot.
239 *
240 * @note if the value of RT_TICK_PER_SECOND is lower than 1000 or
241 * is not an integral multiple of 1000, this function will not
242 * provide the correct 1ms-based tick.
243 *
244 * @return Return passed millisecond from boot.
245 */
rt_tick_get_millisecond(void)246 rt_weak rt_tick_t rt_tick_get_millisecond(void)
247 {
248 #if RT_TICK_PER_SECOND == 0 /* make cppcheck happy*/
249 #error "RT_TICK_PER_SECOND must be greater than zero"
250 #endif
251
252 #if 1000 % RT_TICK_PER_SECOND == 0u
253 return rt_tick_get() * (1000u / RT_TICK_PER_SECOND);
254 #else
255 #warning "rt-thread cannot provide a correct 1ms-based tick any longer,\
256 please redefine this function in another file by using a high-precision hard-timer."
257 return 0;
258 #endif /* 1000 % RT_TICK_PER_SECOND == 0u */
259 }
260
261 /**@}*/
262