FreeRTOS

General

    •  The below statement doesn’t make sense to me. (Taken from the official documentation https://www.freertos.org/implementation/a00012.html)

      The ‘__attribute__ ( ( signal ) )’ directive on the function prototype informs the compiler that the function is an ISR and results in two important changes in the compiler output.

    • The ‘signal’ attribute ensures that every processor register that gets modified during the ISR is restored to its original value when the ISR exits. This is required as the compiler cannot make any assumptions as to when the interrupt will execute, and therefore cannot optimize which processor registers require saving and which don’t.

The compiler can add a code of a “context save” at the beginning of the function and “context restore” at the end of the function (ISR in this case).
Moreover, in the same documentation, on the previous page, it is shown that after ISR a different task runs. The context switch for that task is done in the ISR itself. If point 1 is true then context switch will be overwritten.

Update
Documentation is making sense if I see the https://www.freertos.org/implementation/a00014.html
But not completely.
Update 2
After reading the “Detailed example” and whole section, it seems clear now.

  • The use of gcc attribute “naked” is understandable.

    When the ‘naked’ attribute is used the compiler does not generate any function entry or exit code so this must now be added explicitly. The RTOS macros portSAVE_CONTEXT() and portRESTORE_CONTEXT() respectively save and restore the entire execution context.:

    void SIG_OUTPUT_COMPARE1A( void ) __attribute__ ( ( signal, naked ) );
    void SIG_OUTPUT_COMPARE1A( void )
    {
        /* Macro that explicitly saves the execution context. */
        portSAVE_CONTEXT();
        /* ISR C code for RTOS tick. */
        vPortYieldFromTick();
        /* Macro that explicitly restores the execution context. */
        portRESTORE_CONTEXT();
        /* The return from interrupt call must also be explicitly added. */
        asm volatile ( "reti" );
    }

    The ‘naked’ attribute gives the application code complete control over when and how the AVR context is saved. If the application code saves the entire context on entering the ISR there is no need to save it again before performing a context switch so none of the processor registers get saved twice.

    The above statement makes sense to me only if the application code they meant is – the code in the ISR. Which is vPortYieldFromTick function call in the above example.

Scheduling

  • If the number of tasks are less than 32 then below option would be helpful. (To save RAM)

If the port in use implements a port optimised task selection mechanism that uses a ‘count leading zeros’ type instruction (for task selection in a single instruction) and configUSE_PORT_OPTIMISED_TASK_SELECTION is set to 1 in FreeRTOSConfig.h, then configMAX_PRIORITIES cannot be higher than 32. In all other cases configMAX_PRIORITIES can take any value within reason – but for reasons of RAM usage efficiency should be kept to the minimum value actually necessary.

  • Same priority tasks would be a good option but it would make the system less predictable? hard to imagine worst case scenarios?

Any number of tasks can share the same priority. If configUSE_TIME_SLICING is not defined, or if configUSE_TIME_SLICING is set to 1, then Ready state tasks of equal priority will share the available processing time using a time sliced round robin scheduling scheme.

  • By default, FreeRTOS uses a fixed-priority preemptive scheduling policy, with round-robin time-slicing of equal priority tasks.
  • AMP – asymmetric multiprocessing where a whole instance of RTOS runs on both processors.
    SMP – symmetric multiprocessing where a single instance of RTOS runs and schedules 2 different tasks on 2 different cores. (assuming it is dual-core)
    In both cases, memory should be shared.
  • Irrespective of AMP or SMP – the below options give us different possibilities.
    – configUSE_PREEMPTION
    – configUSE_TIME_SLICING
  • Below 2 flags are only for the processor with multiple core. If we want to port AMP RTOS or single-core RTOS to SMP then we can do it without affecting the functionality by usingconfigRUN_MULTIPLE_PRIORITIES – As its name suggests, it allows multiple priority tasks to run at the same time. Turning it to 0 will cause only the same priority tasks to run at the same time.
    So 2 tasks with the same priority would run in parallel. If it is developed from porting point of view, then we have to consider whether the tasks were truly parallel before or they were using time slicing. configUSE_CORE_AFFINITY – If this flag is set, we can define on which core a task can or can not run.

Co-routine

  • The prototype of co-routine is
    void vACoRoutineFunction( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )It is written in official documentation that “Many co-routines can be created from a single co-routine function. The uxIndex parameter is provided as a means of distinguishing between such co-routines.” – do we need to put if-else in the definition of co-routine?
  • Co-routines share the stack. They are more restrictive to use than the task.
    – You can not have a local variable. You need to make it static.
    – Call to make a block can only be made from co-routine. If you call another function from co-routine and insert a blocking call, it is not allowed. Why?
    –  It is not permitted to make a blocking call within a switch statement. Why?
    How the last 2 situations would cause a problem because of the shared stack, I have not understood this.
  • Co-routines have lower priority than tasks and they are usually invoked from idle task.
  • To schedule the co-routines, we need to make repeated calls to vCoRoutineSchedule() function through idle task hook.
    To enable the idle task hook, we need to set configUSE_IDLE_HOOK to 1.

IPC – Notifications

  • It is written in the official documentation is that notifications are memory efficient, but they come at the cost of –
    1. No broadcast notifications possible
    2. The entity which is notifying could not get blocked for the acknowledgment.

Stream and message buffers

  • Message buffers are built on top of stream buffers.
  • Both are designed for 1-1 communication, not 1-many. Either between task-task or interrupt-task.
  •  Stream buffers pass a continuous stream of bytes and messages pass discrete messages of variable size.

Software Timers

  • In order to use software timer feature, we need to add the file first and then we need to configure some #defines in FreeRTOSConfig.h file.
  • The functionality of timer is nothing special compared to general timer functionality concepts such as one-shop and auto-reload.
  • One thing to keep in mind is that we can not block in the timer callback context because it is invoked from “timer daemon service task” and we can not block the daemon task.
  • There is only one timer daemon task and it invokes all the callbacks.

Event Groups

  • There is nothing special about event groups. In the end, they are just “flags” that can be used for different purposes. For example, each bit of the 32-bit variable is a “flag” that can be set or reset.
  • Some configurations make it 8-bit or 24-bit.
  • As usual, blocking unblocking of tasks can take place with the help of flags.

Source Organization

  • In the root directory, there are 2 folders – Demo and Source.
  • In the source directory, there are – source files, include folder and portable folder.
  • In the portable folder, there are  –
    compiler-specific folders, and architecture specific sub-folders.
    A folder named “MemMang” for memory management.
    We need to only keep above 2 folders and we can delete all other folders.
  • In the Demo folder, there are – compiler specific folders and a common folder. We need to only keep them both and we can delete all other folders.

FreeRTOSConfig.h

It is a huge file. We can see our specific details in the official documentation.

Static Vs Dynamic Memory

  • Either one or both the methods can be used to allocate memory in a single application
  • By default, dynamic allocation is enabled.
  • Benefit of static allocation is – The maximum RAM footprint can be determined at link time, rather than run time. – to learn more about static allocation, please refer to standard demo/test task StaticAllocation.c.

GCC Commands

  • -Wp: it means, instead of giving the commands to “compiler driver”, give it directly to pre-processor.
    note: gcc itself is a set of various tools such as compiler, linker, pre-processor etc.
  • MD:is equivalent to -M -MF file, except that -E is not implied. (use of -E: if you use the -E option, nothing is done except preprocessing)
    • -M: Instead of outputting the result of preprocessing, output a rule suitable for make describing the dependencies of the main source file. The preprocessor outputs one make rule containing the object file name for that source file, a colon, and the names of all the included files.
      e.g.:
      target:
      gcc -Wp,-MD,target_different.o.d target.c
      A new file will be generated named “target_different.o.d” and it will contain below text:
      target.o: target.c /usr/include/stdc-predef.h include1.h
  • -MT: changes the target name in the file created by -MD
    • e.g.:
      target:
      gcc -Wp,-MD,target_different.o.d \
      -Wp,-MT,target_name_changed.o \
      -c -o target_different.o target.c
      Notice the changed name of target because of -MT:
      target_name_changed.o: target.c /usr/include/stdc-predef.h include1.h

MAKEFILE

  • The whole thing below is called as a rule:
    targets: prerequisites
        command
    (Notice the tab before command. It is must)

    • “Target” and “prerequisites” are files.
      Make program checks the timestamps of “Target” and “prerequisites”. If Target is older than “Prerequisites”, then it executes the command to create a new target. It will make the timestamp of Target to newer than prerequisites. If you run make again, command will not be executed because timestamp of Target would be greater than timestamp of prerequisite.

LINKER

  • Linker script is divided in different commands.
    • First memory is defined by using MEMROY command.
      It contains
      name (attr) : ORIGIN = origin. LENGTH = length
      e.g.
      MEMORY
      {
      rom (rx) : ORIGIN = 0, LENGTH = 256K
      ram (!rx) : org = 0x40000000, l = 4M
      }

Leave a Comment

Your email address will not be published. Required fields are marked *