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