(5) Betaflight is transplanted to keil – running on BF_OS

Table of Contents

5.6.4.1 __attribute__ tool

5.6.4.2 scheduler(); function

(1) micros() function

1. register keyword

2. VECTACTiVE (Active vector) of SCB (System Control Block) ICSR (Interrupt Control and Status Register)

3. About __get_BASEPRI() referencing #include “platform.h”

4. microsISR() function

(2) inline inline function keyword

(3) Systick is used to calculate task running time?

(4) schedulerExecuteTask execution task function

(5) Scan task queue


BF_OS implements task debugging by running the run(); function in the While loop. The prototype of run(); is

void FAST_CODE run(void)

{

    while (true) {

// scheduler();

#ifdef SIMULATOR_BUILD

        delayMicroseconds_real(50); // max rate 20kHz

#endif

    }

}

Note: Perhaps due to different compilation environments, some .h files in the BF source code lack certain macro definitions, and you need to use #include to reference the corresponding header files. Preliminary guess is that it is because gun under Linux assigns “global” attributes to certain macro definitions during compilation.

5.6.4.1 __attribute__ tool
void FAST_CODE There is a FAST_CODE macro definition in the definition of run(void), which is defined as:

#define FAST_CODE __attribute__((section(".tcm_code"))) __attribute__((optimize(ITCM_RAM_OPTIMISATION)))

__attribute__, well I guess it should be regarded as a macro definition, which can specify some operations to the compiler in C language, such as attributes used to control variables and functions, and specify the address and segment address of variables. The BF source code is compiled with the GUN compiler under Linux, while Keil MDK uses ARMCC. Both have this tool, but the specific functions are different. For example, ARMCC does not seem to have a definition of “optimize”, which is used to set the program optimization level to -O2. Keil has a definition in the “magic wand” (target option). Change the macro definition here to:

#define FAST_CODE __attribute__((section(".tcm_code")))

__attribute__ usage reference:

Control variable and function attributes, and specify variable addresses and segment addresses in Keil MDK_WellAndy’s Blog-CSDN Blog

Keil optimization level description reference:

Record the optimization level and description of the KEIL compiler_What is the appropriate setting for the keil optimization level-CSDN Blog

Here, the Keil program optimization level is set to level -O1, and we will try to change it to level -O2 later.

5.6.4.2 scheduler(); function

The function prototype is a bit long:

FAST_CODE void scheduler(void)

{

    static uint32_t checkCycles = 0;

    static uint32_t scheduleCount = 0;

#if !defined(UNIT_TEST)

    const timeUs_t schedulerStartTimeUs = micros();

#endif

    timeUs_t currentTimeUs;

    uint32_t nowCycles;

    timeUs_t taskExecutionTimeUs = 0;

    task_t *selectedTask = NULL;

    uint16_t selectedTaskDynamicPriority = 0;

    uint32_t nextTargetCycles = 0;

    int32_t schedLoopRemainingCycles;

…

}
(1) micros() function

Defined in “\src\main\drivers\system.c”, the prototype is:

uint32_t micros(void)

{

    register uint32_t ms, cycle_cnt; //Save the value of the variable in the kernel general register



    // Call microsISR() in interrupt and elevated (non-zero) BASEPRI context

//Exception number of interrupt control and status register of SCB (system control block)

    if ((SCB->ICSR & amp; SCB_ICSR_VECTACTIVE_Msk) || (__get_BASEPRI())) {

        return microsISR();

    }



    do {

        ms = sysTickUptime;

        cycle_cnt = SysTick->VAL;

    } while (ms != sysTickUptime || cycle_cnt > sysTickValStamp);



    return (ms * 1000) + (usTicks * 1000 - cycle_cnt) / usTicks;

}

The declaration of micros(void) is another pitfall. The uint32_t micros(void) function is not declared in its own “system.h” header file, but is declared in “\src\main\drivers\time.h” :

timeUs_t micros(void);

timeUs_t microsISR(void);

timeMs_t millis(void);

timeUs_t is a typedef type definition, which is actually the uint32_t type. So, if you want to use this micros function, use #include “drivers/time.h” instead of #include “drivers/system.h”

1. register keyword

Generally, the values of variables defined in a program will be sequentially called into memory after the processor is started and the program is run. However, variables defined using register are stored in the processing core general registers (i.e. R0~R13) when called, which means that the variable will be used as a register variable, allowing the variable to be accessed as quickly as possible.

refer to:

C language – register_c language register-CSDN blog

2. SCB (System Control Block) VECTACTiVE (Active vector) of ICSR (Interrupt Control and Status Register)

The bits in this register store the currently active “exception number”. “Exception number” is the serial number of various defined “exceptions” in the processor, including reset, Systick, peripheral interrupt (IRQ), etc. Usually the interrupts encountered in 32 learning are peripheral interrupts (IRQ), and IRQ is only an exception in the Cotex-M4 core, and the specific implementation is designed by the chip manufacturer. Of course, “exceptions” have their own interrupts, but there is still a difference between the two. The two are causally related. An exception does not necessarily cause an interrupt. The numbering of interrupts and exceptions are also two systems.

Cotex-M4 assigns a number to each exception,

For details, please refer to the chapters “2.3.2 Exception types” and “4.4.3 Interrupt control and state register (ICSR)” in “2. References\STM32F3 and F4 Series Cortex M4 Core Programming Manual.pdf”.

3. Regarding the reference to #include “platform.h”‘s __get_BASEPRI ()

The “platform.h” file is the “platform” header file, which contains calls to the AT32VMT7 library header files, and the AT32 library references some core kernel library header files and cmsis (standard interface) library header files.

To sum up, __get_BASEPRI() is a macro definition in the cmsis header file, located in “\lib\main\AT32F43x\cmsis\cm4\core_support\cmsis_armcc.h”. The kernel register “BASEPRI” can be obtained through assembly language. value.

The value saved by BASEPRI is also guessed to be the “abnormal number” mentioned before. First of all, the “abnormal” interrupt corresponding to the BASEPRI value has a priority, such as n, then the processor will screen interrupts with a priority greater than or equal to n. When BASEPRI is 0, this register has no effect.

BASEPRI defaults to 16, which is the exception number of IRQ?

4. microsISR() function

The function prototype is:

uint32_t microsISR(void)

{

    register uint32_t ms, pending, cycle_cnt;



    ATOMIC_BLOCK(NVIC_PRIO_MAX) {

        cycle_cnt = SysTick->VAL;



        if (SysTick->CTRL & amp; SysTick_CTRL_COUNTFLAG_Msk) {

            // Update pending.

            // Record it for multiple calls within the same rollover period

            // (Will be cleared when serviced).

            // Note that multiple rollovers are not considered.



            sysTickPending = 1;



            // Read VAL again to ensure the value is read after the rollover.



            cycle_cnt = SysTick->VAL;

        }



        ms = sysTickUptime;

        pending = sysTickPending;

    }



    return ((ms + pending) * 1000) + (usTicks * 1000 - cycle_cnt) / usTicks;

}

ATOMIC_BLOCK is a macro definition, which is guessed to be used for atomic operations.

Preliminary speculation is that its function is to obtain the current us-level Systick count value, which includes calling core registers such as SCB and BASEPRI. The specific function is not yet clear.

Note: “us level” here refers to converting the corresponding clock count into us.

(2) inline inline function keyword

There is a program in line 437

schedLoopRemainingCycles = cmpTimeCycles(nextTargetCycles, nowCycles); //Return next - now

The cmpTimeCycles function is called, which is a function defined in “\src\main\common\time.h”. The prototype is:

static inline int32_t cmpTimeCycles(uint32_t a, uint32_t b) { return (int32_t)(a – b); }

The inline function keyword is used. The function defined by this keyword has very similar functions to the macro definition. During compilation, the content of the defined function will be copied to the calling location and then compiled.

For example:

c = cmpTimeCycles(a, b );

It actually compiles to:

c = a – b;

This avoids the consumption caused by repeated allocation of stack memory by frequently calling functions. However, inlining comes at the expense of code expansion (duplication), and only saves the overhead of function calls, thus improving the execution efficiency of the function.

refer to:

Inline function – Usage analysis of keyword inline in C (reprint)_c inline-CSDN blog

(3) Systick is used to calculate task running time?
(4) schedulerExecuteTask execution task function

parameter:

task_t *selectedTask task control block pointer

timeUs_t currentTimeUs current us level OS count value

The function is to execute the task function corresponding to the input task control block. The function prototype is:

(5) Scanning task queue

In line 612 of the program, task queue scanning is performed.

 for (task_t *task = queueFirst(); task != NULL; task = queueNext()) {

    …

}