Abstract:This article led us to analyze together the HongMeng light kernel time management module source code. The time management module provides the necessary clock beats for task scheduling and provides all time-related services to the application, such as time conversion, statistics, and delay functions.

This article is shared from HUAWEI cloud community “HongMeng light kernel M kernel source analysis series six time management”, the original author: Zhushy.

In this article, we will continue our analysis of the Tick and Time-related source code and introduce you to the time management module of the Hongmeng Light Kernel. In this article the source involved in OpenHarmony LiteOS -m kernel, for example, can be in the open source site https://gitee.com/openharmony… To obtain.

The time management module is based on the system clock and can be divided into two parts. One part is SYSTICK interrupt, which provides the necessary clock beat for task scheduling. The other part is to provide the application with all time-related services, such as time conversion and statistics.

The system clock is generated by an interrupt triggered by an output pulse generated by the timer/counter and is generally defined as an integer or a long integer. The period of the output pulse is called a “clock Tick,” also known as the time mark or Tick. Tick is the operating system’s basic unit of time, determined by the number of ticks per second configured by the user. If the user configures the number of ticks per second to be 1000, then 1 Tick equals 1ms of time. Another unit of time is the Cycle, which is the smallest unit of time in the system. The duration of Cycle is determined by the frequency of the system main clock, which is the number of cycles per second. For a CPU of 216 MHz, 216000000 cycles are generated in 1 second.

Users are timed in seconds and milliseconds, while the operating system is timed in ticks. When users need to operate on the system, such as task suspension, delay, etc., they can use the time management module to convert Tick to seconds/milliseconds.

Next, we analyze the source code of the time management module. If it involves the part of the development board, take the development board project targets\cortex-m7_nucleo_f767zi_gcc\ as an example to carry out source code analysis.

1. Time management initialization and startup

Let’s take a look at the configuration of the time management module, and then look at how it is initialized and started.

1.1 Configuration related to time management

The time management module involves three configuration items, the system clock OS_SYS_CLOCK, the number of ticks per second LOSCFG_BASE_CORE_TICK_PER_SECOND, and the macro LOSCFG_BASE_CORE_TICK_HW_TIME. Loscfg_base_core_tick_hw_time is turned off by default. When turned on, you need to provide the custom VOID platform_tick_handler(VOID) to perform the custom action in the Tick interrupt handler. These configuration items are defined in the file target_config.h in the project directory of the template development board, such as the file Targets \cortex-m7_nucleo_f767zi_gcc\target_config.h as follows:


#define OS_SYS_CLOCK                                        96000000
#define LOSCFG_BASE_CORE_TICK_PER_SECOND                    (1000UL)
#define LOSCFG_BASE_CORE_TICK_HW_TIME                       0

1.2 Time management initialization and startup

The function INT32 main(VOID) starts the system by calling the function UINT32 LOS_Start(VOID) in kernel\ SRC \los_init.c. This function calls the start scheduling function UINT32 halstartSchedule (OS_TICK_HANDLER handler). The source is as follows:

LITE_OS_SEC_TEXT_INIT UINT32 LOS_Start(VOID)
{
    return HalStartSchedule(OsTickHandler);
}

The function UINT32 HalTickStart(OS_TICK_HANDLER *handler) is defined in kernel\arch\arm\ tex-m7\ GCC \los_context.c. The source code is as follows. The function parameter is the Tick interrupt handler ostickHandler (), which will be examined later. (1) The code continues to call the function and further calls the function haltickStart (handler) to set Tick to interrupt. (2) The assembly function HalStartToRun will be called to start running the system, and the assembly function will be analyzed in detail in the subsequent task scheduling series.

LITE_OS_SEC_TEXT_INIT UINT32 HalStartSchedule(OS_TICK_HANDLER handler) { UINT32 ret; (1) ret = HalTickStart (handler); if (ret ! = LOS_OK) { return ret; } [2] HalStartToRun (); return LOS_OK; /* never return */ }

The function haltickStart (handler) is defined in the file kernel\arch\arm\ tex-m7\ GCC \los_timer.c. The source code is as follows. (1) Verify the validity of the configuration items of the time management module. When the macro LOSCFG_USE_SYSTEM_DEFINED_INTERRUT is turned on, a system-defined interrupt is used. Set the interrupt vector by calling ossetVector (), a function defined in the file kernel\arch\arm\ tex-m7\ GCC \los_interrupt.c, which is analyzed in detail in the interrupt series. ⑶ Set the global variable g_sysClock as the system clock, g_cyclesPerTick as the number of cycles corresponding to each tick, and initialize g_ulltickCount to 0, indicating the number of system tick interruptions. The inline function defined in targets\cortex-m7_nucleo_f767zi_gcc\Drivers\CMSIS\Include\core_cm7.h is called uint32_t SysTick_Config(uint32_t) Ticks), initializes and starts the system timer Systick and interrupts.

WEAK UINT32 HalTickStart(OS_TICK_HANDLER *handler) { UINT32 ret; (1) if ((OS_SYS_CLOCK = = 0) | | (LOSCFG_BASE_CORE_TICK_PER_SECOND = = 0) | | (LOSCFG_BASE_CORE_TICK_PER_SECOND > OS_SYS_CLOCK)) { return LOS_ERRNO_TICK_CFG_INVALID; } #if (LOSCFG_USE_SYSTEM_DEFINED_INTERRUPT == 1) #if (OS_HWI_WITH_ARG == 1) OsSetVector(SysTick_IRQn, (HWI_PROC_FUNC)handler, NULL); # the else 2 OsSetVector (SysTick_IRQn, HWI_PROC_FUNC handler); #endif #endif ⑶ g_sysClock = OS_SYS_CLOCK; g_cyclesPerTick = OS_SYS_CLOCK / LOSCFG_BASE_CORE_TICK_PER_SECOND; g_ullTickCount = 0; Ret [4] = SysTick_Config (g_cyclesPerTick); if (ret == 1) { return LOS_ERRNO_TICK_PER_SEC_TOO_SMALL; } return LOS_OK; }

OstickHandler ()

The function VOID OsTickHandler(VOID), defined by the file kernel\ SRC \los_tick.c, is the most frequently executed function in the time management module and is called whenever a Tick interrupt occurs. (1) If the macro LOSCFG_base_core_tick_hw_time is turned on, the custom tick handler function platform_tick_handler() will be called. By default, it will not be turned on. ⑶ If the macro LOSCFG_BASE_CORE_TIMESLICE is enabled, the time slice of the current running task will be checked. The function osTimesliceCheck () will be analyzed in detail in the following task module. The sorted linked list of tasks will be traverse to check whether there is any task that has timed out. ⑸ Check if the timer has expired if the timer feature is supported.

The source is as follows:

LITE_OS_SEC_TEXT VOID OsTickHandler(VOID) {#if (LOSCFG_BASE_CORE_TICK_HW_TIME == 1) ⑴ platform_tick_handler(); # endif g_ullTickCount [2] + +; #if (LOSCFG_BASE_CORE_TIMESLICE == 1) ⑶ osTimesliceCheck (); # endif OsTaskScan [4] (); // task timeout scan #if (LOSCFG_BASE_CORE_SWTMR == 1) ⑸ (VOID)OsSwtmrScan(); #endif }

2, LiteOS kernel time management common operations

Time management provides the following functions, such as time conversion, time statistics, and so on. These functions are defined in the file kernel\ SRC \los_tick.c. Let’s take a look at the source code implementation of these operations.

2.1 Time conversion operation

2.1.1 milliseconds converted to Tick

The function uint32los_ms2tick (uint32millisec) takes the number of milliseconds input parameter uint32millisec and converts it to the number of ticks. OS_SYS_MS_PER_SECOND in the code, i.e. 1 second equals 1000 milliseconds. If you know the number of ticks per second, divide by OS_SYS_MS_PER_SECOND to get the number of ticks per millisecond, then multiply by millisec to calculate the resulting number of ticks and return it.

LITE_OS_SEC_TEXT_MINOR UINT32 LOS_MS2Tick(UINT32 millisec)
{
    if (millisec == OS_NULL_INT) {
        return OS_NULL_INT;
    }

    return ((UINT64)millisec * LOSCFG_BASE_CORE_TICK_PER_SECOND) / OS_SYS_MS_PER_SECOND;
}

2.1.2 Tick to milliseconds

The function uint32los_tick2ms (uint32tick) converts the number of ticks to milliseconds as an input parameter. The time conversion is also easy. The ticks divided by the ticks per second LOSCFG_BASE_CORE_TICK_PER_SECOND calculates how many seconds, then converts it to milliseconds, calculates the result and returns it.

LITE_OS_SEC_TEXT_MINOR UINT32 LOS_Tick2MS(UINT32 ticks)
{
    return ((UINT64)ticks * OS_SYS_MS_PER_SECOND) / LOSCFG_BASE_CORE_TICK_PER_SECOND;
}

2.1.3 Cycle number is converted to milliseconds

Before we introduce the conversion function, let’s look at the next Cputick structure. The structure is relatively simple, with two members representing the high and low 32-bit values of a UINT64 data type.

typedef struct tagCpuTick { UINT32 cntHi; /* < a 64-bit value of the high 32-bit */ uint32cntlo; /* < a 64-bit value of the low 32-bit */} CpuTick;

Moving on to the conversion function OsCpuTick2MS(), which converts the number of cycles represented by the CpuTick type into the corresponding number of milliseconds, outputs the high and low 32-bit values of the millisecond data. (1) Check whether the parameter is a null pointer and (2) Check whether the system clock is configured. ⑶ The number of cycles expressed by Cputick structure is converted to UINT64 data. The number of cycles per millisecond is obtained by (DOUBLE)g_sysClock/OS_SYS_MS_PER_SECOND, and then the number of milliseconds corresponding to the number of cycles is obtained by division with tmpCpuTick. ⑸ convert DOUBLE type to UINT64 type, and then perform ⑹, assigning the high and low 64 bits of the result value to MSLO and MSHI, respectively.

LITE_OS_SEC_TEXT_INIT UINT32 OsCpuTick2MS(CpuTick *cpuTick, UINT32 *msHi, UINT32 *msLo) { UINT64 tmpCpuTick; DOUBLE temp; (1) if ((cpuTick = = NULL) | | (msHi = = NULL) | | (msLo = = NULL) {return LOS_ERRNO_SYS_PTR_NULL; } ⑵ if (g_sysClock == 0) {LOS_ERRNO_SYS_CLOCK_INVALID;} ⑵ if (g_sysClock == 0) {LOS_ERRNO_SYS_CLOCK_INVALID; } (3) tmpCpuTick = ((UINT64) cpuTick - > cntHi < < OS_SYS_MV_32_BIT) | cpuTick - > cntLo; ⑷ temp = tmpCpuTick/((DOUBLE)g_sysClock/OS_SYS_MS_PER_SECOND); tmpCpuTick = (UINT64)temp; *msLo = (UINT32)tmpCpuTick; *msHi = (UINT32)(tmpCpuTick >> OS_SYS_MV_32_BIT); return LOS_OK; }

2.1.4 Cycle number is converted to microseconds

Conversion function OsCpuTick2US(), which can convert the number of cycles represented by Cputick type into the corresponding number of milliseconds, outputs the high and low 32-bit values of millisecond data. This function is similar to OsCpuTick2MS() and can be read on your own.

LITE_OS_SEC_TEXT_INIT UINT32 OsCpuTick2US(CpuTick *cpuTick, UINT32 *usHi, UINT32 *usLo)
{
    UINT64 tmpCpuTick;
    DOUBLE temp;

    if ((cpuTick == NULL) || (usHi == NULL) || (usLo == NULL)) {
        return LOS_ERRNO_SYS_PTR_NULL;
    }

    if (g_sysClock == 0) {
        return LOS_ERRNO_SYS_CLOCK_INVALID;
    }
    tmpCpuTick = ((UINT64)cpuTick->cntHi << OS_SYS_MV_32_BIT) | cpuTick->cntLo;
    temp = tmpCpuTick / ((DOUBLE)g_sysClock / OS_SYS_US_PER_SECOND);

    tmpCpuTick = (UINT64)temp;

    *usLo = (UINT32)tmpCpuTick;
    *usHi = (UINT32)(tmpCpuTick >> OS_SYS_MV_32_BIT);

    return LOS_OK;
}

2.2 Time statistics operation

2.2.1 Obtain the number of cycles per Tick

The function uint32los_cyclepertickget (VOID) calculates how many cycles a tick is. G_SYSCLOCK system clock represents cycles in 1 second, LOSCFG_BASE_Core_tick_per_second number of ticks in 1 second, divided by 1 tick number of cycles, That is g_cyclesPerTick = g_sysClock/LOSCFG_BASE_CORE_TICK_PER_SECOND.

LITE_OS_SEC_TEXT_MINOR UINT32 LOS_CyclePerTickGet(VOID)
{
    return g_cyclesPerTick;
}

2.2.2 Obtain the number of ticks since system startup

The uint64los_tickcountGet (VOID) function counts the number of Tick interrupts since the system started. Note that counting is not performed in the case of a closed interrupt and cannot be used as an exact time. Each time a Tick interrupt occurs, the g_ulltickCount data is updated in the function VOID ostickHandler (VOID).

LITE_OS_SEC_TEXT_MINOR UINT64 LOS_TickCountGet(VOID)
{
    return g_ullTickCount;
}

2.2.3 Obtain the system clock

The uint32los_sysclockget (VOID) function gets the configured system clock.

UINT32 LOS_SysClockGet(VOID)
{
    return g_sysClock;
}

2.2.4 Obtain the number of cycles since system start-up

The function VOID HalGetCpuCycle(uint32cnthi, uint32cntlo) is defined in the file kernel\arch\arm\ cortic-m7 \ GCC \los_timer.c. This function gets the number of cycles of the system since startup. Returns the result as high, low 32-bit unsigned value UINT32 CNTHI, UINT32 CNTLO return respectively.

Let’s look at the source code for this function. (1) Gets the number of ticks since the start of the startup. ⑵ Get HWCycle by reading the Current Value Register SysStick Current Value Register. If the Tick_Check bit of the State Register is 1, the SysStick Interrupt is suspended. The tick does not count and needs to be calibrated by adding 1. (4) Calculate the number of cycles since the system started according to swTick, g_cyclesPerTick and hwCycle. (⑸) to get the unsigned value of the high and low 32 bits of the Cycle number, and then open interrupt and return.

LITE_OS_SEC_TEXT_MINOR VOID HalGetCpuCycle(UINT32 *cntHi, UINT32 *cntLo) { UINT64 swTick; UINT64 cycle; UINT32 hwCycle; UINTPTR intSave; intSave = LOS_IntLock(); (1) swTick = g_ullTickCount; 2 hwCycle = SysTick - > VAL. ⑶ if (SCB->ICSR & TICK_CHECK)! = 0) { hwCycle = SysTick->VAL; swTick++; } ⑷ cycle = ((((swTick) * g_cyclesPerTick) + (g_cyclespertick-hwcycle)); ⑸ *cntHi = cycle >> SHIFT_32_BIT; *cntLo = cycle & CYCLE_CHECK; LOS_IntRestore(intSave); return; }

summary

This article led us to analyze together the HongMeng light kernel time management module source code. The time management module provides the necessary clock beats for task scheduling and provides all time-related services to the application, such as time conversion, statistics, and delay functions. Subsequent will launch more share articles, please look, also welcome you to share the experience of learning, light use HongMeng kernel and if you have any questions, Suggestions, can leave a message to us: https://gitee.com/openharmony… . In order to more easily find HongMeng kernel code warehouse, suggest to https://gitee.com/openharmony… , follow Watch, thumb up Star, and Fork to your account, thank you.

Click on the attention, the first time to understand Huawei cloud fresh technology ~