Search by Tags

FreeRTOS on the Cortex-M4 of a Colibri VF61


Article updated at 28 Jun 2022
Compare with Revision

Subscribe for this article updates

The NXP/Freescale VF6xx SoC which is the core of the Colibri VF61 module implements a heterogeneous asymmetric architecture. Besides the main CPU core based on the ARM Cortex-A5 processor, a secondary general purpose ARM Cortex-M4 core is available too. The secondary core typically runs a RTOS optimized for microcontrollers or a bare-metal application. Toradex provides FreeRTOS™, a free professional grade real-time operating system for microcontrollers, along with drivers and several examples which can be used on our Colibri VF61 platform. The FreeRTOS™ port is based on NXP FreeRTOS BSP for i.MX 7.

The source code is available at:


The Cortex-M4 CPU core lives side by side with the Cortex-A5 based primary CPU core. Both CPU complexes have access to the same interconnect and hence have equally access to all peripherals (shared bus topology).

There are several types of memory available. The Cortex-M4 provides local memory (Tightly Coupled Memory, TCM), which is relatively small but can be accessed by the CPU without any latency. There are multiple OCRAM areas (On-Chip RAM, typically SRAM) which are relatively fast as well and slightly larger. The third option is the DDR3 based main memory. From a performance perspective one of the internal areas should be selected whenever possible.

A traditional microcontroller typically has internal NOR flash where the firmware is stored and executed from. This is not the case on Colibri VF61: There is no NOR flash where the firmware can be flashed onto. Instead, the firmware needs to be stored on the mass storage device such as SD-card or the internal NAND flash. The available mass storage devices are not "memory mapped", and hence application can not be executed directly from any of the cores (no eXecuted-In-Place, XIP). Instead, code need to be loaded into one of the available memory sections before the CPU can start executing it.

The Colibri VF61 always boots using the Cortex-A5 core. The core executes the internal boot ROM which typically loads a boot loader such as U-Boot. The boot loader allows loading the firmware from the mass storage device (e.g. NAND flash) into memory, and triggers the Cortex-M4 to start executing the firmware. To upgrade or replace a firmware, one can just replace the firmware binary on the mass storage device.

Memory areas

The two CPU platforms use a different memory layout to access individual sub systems. This table lists some important areas and their memory location for each of the cores side by side. The full list can be found in the Vybrid reference manual.

Region Size Cortex-A5 Cortex-M4 (System Bus) Cortex-M4 (Code Bus)
Boot ROM 96KB 0x00000000-0x00018000 0x00000000-0x00018000
DDR Address 2048MB (less for M4) 0x80000000-0xffffffff 0x80000000-0xdfffffff 0x00800000-0x0fffffff (maps to 0x80800000-0x8fffffff)
OCRAM 512KB 0x3f000000-0x3f07ffff 0x3f000000-0x3f07ffff 0x1f000000-0x1f07ffff (part of 8MB area 0x1f000000-0x1f7fffff)
TCML 32KB 0x1f800000-0x1f808000 0x1f800000-0x1f808000
TCMU 32KB 0x3f800000-0x3f808000 0x3f800000-0x3f808000

Get the FreeRTOS Source Code

The FreeRTOS source code is available on our git server. Use git to clone the repository:

# git clone -b colibri-vf61-m4-freertos-v8

This table shows how the FreeRTOS BSP source code is structured:

Directory Content
doc/ NXP/Freescales FreeRTOS BSP Documentation
examples/ Application examples
examples/vf6xx_colibri_m4/ Examples ported to Toradex Colibri VF61
middleware/multicore/open-amp/ OpenAMP based RPMsg stack (remote messaging framework)
platform/ Driver library, startup code and utilities
platform/CMSIS/ Cortex Microcontroller Software Interface Standard (CMSIS) ARM Cortex®-M header files, DSP library source
platform/devices/VF6XX/linker/ Linker control files for each supported toolchain
platform/drivers/ Peripheral Drivers
platform/utilities/ Utilities such as debug console
rtos/FreeRTOS/ FreeRTOS Kernel folder

Support on Linux

GNU Makefiles and CMake with GCC

Using GNU Makefiles and CMake along with the GCC compiler has been tested on Linux based host systems.


We recommend using one of the Linaro provided ARM Embedded toolchains, e.g. 4.9 2015 Q3 update. Unpack the toolchain to an appropriate location, e.g. your home directory:

$ tar xjf ~/Downloads/gcc-arm-none-eabi-4_9-2015q3-20150921-linux.tar.bz2

Since the toolchain is compiled as 32-bit binaries, make sure to install 32-bit version of libc and libncurses. On Ubuntu 14.04 the commands to install those libraries are:

sudo dpkg --add-architecture i386
sudo apt-get update
sudo apt-get install libc6:i386 libncurses5:i386

With that, gcc should run fine

$ ~/gcc-arm-none-eabi-4_9-2015q3/bin/arm-none-eabi-gcc --version
arm-none-eabi-gcc (GNU Tools for ARM Embedded Processors) 4.9.3 20150529 (release) [ARM/embedded-4_9-branch revision 227977]
Copyright (C) 2014 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO

Furthermore, the GNU make and cmake need to be available. For example, on Ubuntu:

$ sudo apt-get install make cmake


Each example has a sub-directory called armgcc. Enter this subdirectory and use the provided shell scripts to build the example. The scripts expect the environment variable to point to the Linaro ARM Embedded toolchain:

$ export ARMGCC_DIR=~/gcc-arm-none-eabi-4_9-2015q3/
$ cd examples/vf6xx_colibri_m4/demo_apps/hello_world/armgcc
$ ./
-- TOOLCHAIN_DIR: /home/dvb/gcc-arm-none-eabi-4_9-2015q3/
-- BUILD_TYPE: Debug
-- TOOLCHAIN_DIR: /home/dvb/gcc-arm-none-eabi-4_9-2015q3/
-- BUILD_TYPE: Debug
-- The ASM compiler identification is GNU
-- Found assembler: /home/dvb/gcc-arm-none-eabi-4_9-2015q3//bin/arm-none-eabi-gcc
-- Configuring done
-- Generating done
-- Build files have been written to: /home/dvb/freertos-toradex/examples/vf6xx_colibri_m4/demo_apps/hello_world/armgcc
Scanning dependencies of target hello_world
[  4%] Building C object CMakeFiles/hello_world.dir/home/dvb/freertos-toradex/rtos/FreeRTOS/Source/portable/GCC/ARM_CM4F/port.c.obj
[100%] Linking C executable debug/hello_world.elf
[100%] Built target hello_world

With that, the sub directories release/ and debug/ contain the firmware binaries which can be executed on the target (see the instructions of the individual examples below).

Running a Firmware on Cortex-M4

There are two possible ways to boot the Cortex-M4 core on Vybrid:

  1. From U-Boot using m4boot/bootaux
  2. From Linux using remoteproc

Note: In release image 2.7 Beta1, support for m4boot has been dropped from u-boot and remoteproc is the only method to boot and run firmware on Cortex-M4. In release image 2.7 Beta2, support for bootaux has been added including elf boot support.

Note: The FreeRTOS firmware uses Colibri UART_B as its debugging console. Make sure to connect UART_B to your debugging host and start a serial terminal emulator with a baud rate of 115200 on the serial port.

Linux disables unused clocks by default but it is not aware what clocks are used by the Cortex-M4 core. Use the 'clk_ignore_unused' kernel parameter to avoid clocks getting accidentally disabled. The power management suspend code and the Cortex-M4 firmware typically use the same SRAM location leading to a resource conflict. To prevent the suspend code from overwriting the M4 firmware and/or remoteproc from not being able to load, the SRAM driver should be disabled by passing 'initcall_blacklist=sram_init' kernel parameter:

Colibri VFxx # setenv defargs 'clk_ignore_unused initcall_blacklist=sram_init'

By default, our Linux device tree uses UART_B too, which leads to a external abort when the Linux kernel tries to access UART_B. It is recommended to alter the device tree and disable UART_B using the status property (see Device Tree Customization(901439e4-9c90-11e4-8c91-9e9dd95319f8)). Temporary, the following fdt_fixup command can be use in U-Boot:

Colibri VFxx # setenv fdt_fixup 'fdt addr ${fdt_addr_r} && fdt rm /soc/aips-bus@40000000/serial@40029000'
Colibri VFxx # saveenv

Booting from U-Boot

Using bootaux

With images 2.7b2 and newer you can boot the Cortex-M4 using bootaux. This command has been adopted from Colibri iMX7, and allows to boot bin and elf files directly.

Colibri VFxx # fatload mmc 0:1 ${loadaddr} hello_world.elf
Colibri VFxx # bootaux ${loadaddr}
## Starting auxiliary core at 0x1F0002E1 ...
Colibri VFxx # 
Store a Firmware on Flash and Run it on Boot

Use the following commands to create a UBI volume to store the Cortex-M4 firmware (to free up space for this volume this process will remove the rootfs volume!). The update scripts are required for this steps, make sure you have a SD-card with the image prepared and in your SD-card slot.

Colibri VFxx # run setupdate
Colibri VFxx # setenv create_m4firmware 'ubi part ubi && ubi remove rootfs && ubi create m4firmware 0xe0000 static && run prepare_ubi'
Colibri VFxx # run create_m4firmware
Colibri VFxx # fatload mmc 0:1 ${loadaddr} hello_world.elf
Colibri VFxx # ubi write ${loadaddr} m4firmware ${filesize}
Colibri VFxx # run update_rootfs

Once the UBI volume is in place, a new firmware can be written by just using ubi write:

Colibri VFxx # ubi part ubi
Colibri VFxx # fatload mmc 0:1 ${loadaddr} hello_world.elf
Colibri VFxx # ubi write ${loadaddr} m4firmware ${filesize}

You need to extend the default ubiboot command to load and execute the firmware before starting Linux:

Colibri VFxx # setenv ubiboot 'run setup; setenv bootargs ${defargs} ${ubiargs} ${setupargs} ${vidargs}; echo Booting from NAND...; ubi part ubi && ubi read ${loadaddr} m4firmware && bootaux ${loadaddr} && ubi read ${kernel_addr_r} kernel && ubi read ${fdt_addr_r} dtb && run fdt_fixup && bootz ${kernel_addr_r} - ${fdt_addr_r}'
Colibri VFxx # saveenv

Using m4boot

In images using U-Boot 2015.04 (BSPs before 2.7), one need to use FIT image to boot Cortex-M4 from U-Boot via m4boot.

Creating FIT image:

  • Use the image source file(.its) which describes the contents of the image(the actual binary is specified in the form of path and various properties used during booting). One need to also make sure to add the relevant 'entry' address in the image source file. The entry address will be the entry point address of the binary, one can get the information using readelf.


$ readelf -h release/hello_world.elf | grep Entry
  Entry point address:               0x1f000279

Example image source file (vybridm4.its) for hello_world example:

 * U-Boot FIT image with FreeRTOS blob for Vybrid's Cortex-M4
/ {
    description = "FreeRTOS for Vybrid's Cortex-M4";
    #address-cells = <1>;
    images {
        kernel@1 {
            description = "FreeRTOS for Vybrid";
            data = /incbin/("./hello_world.bin");
            type = "kernel";
            arch = "arm";
            os = "baremetal";
            compression = "none";
            load = <0x3f000000>;
            entry = <0x1f000279>;
            hash@1 {
                algo = "md5";
    configurations {
        default = "config@1";
        config@1 {
            description = "FreeRTOS for Vybrid's Cortex-M4";
            kernel = "kernel@1";

Create the image file using the mkimage command:

$ mkimage -f vybridm4.its vybridm4-freertos.itb

The resulting image file vybridm4-freertos.itb can be now transferred to the target.

  • Copy FIT image on a SD-card and boot using U-Boots m4boot command:
Colibri VFxx # fatload mmc 0:1 ${loadaddr} vybridm4-freertos.itb
Colibri VFxx # tftp ${loadaddr} vybridm4-freertos.itb
Colibri VFxx # m4boot ${loadaddr}
## Loading kernel from FIT Image at 80008000 ...
   Using 'config@1' configuration
   Trying 'kernel@1' kernel subimage
     Description:  FreeRTOS for Vybrid
     Type:         Kernel Image
     Compression:  uncompressed
     Data Start:   0x800080d4
     Data Size:    18628 Bytes = 18.2 KiB
     Architecture: ARM
     OS:           Bare-metal
     Load Address: 0x3f000000
     Entry Point:  0x1f000279
     Hash algo:    md5
     Hash value:   577a4448ce6171faf7b35e85bd154c10
   Loading kernel from 0x800080d4 to 0x3f000000
## Loading ramdisk from FIT Image at 80008000 ...
   Using 'config@1' configuration
Could not find subimage node
## Loading fdt from FIT Image at 80008000 ...
   Using 'config@1' configuration
Could not find subimage node
Booting Cortex-M4 @0x1f000279
Colibri VFxx # 
Store a Firmware on Flash and Run it on Boot

Use the following commands to create a UBI volume to store the Cortex-M4 firmware (to free up space for this volume this process will remove the rootfs volume!):

Colibri VFxx # run setupdate
Colibri VFxx # setenv create_m4firmware 'ubi part ubi && ubi remove rootfs && ubi create m4firmware 0xe0000 static && run prepare_ubi'
Colibri VFxx # run create_m4firmware
Colibri VFxx # fatload mmc 0:1 ${loadaddr} vybridm4-freertos.itb
Colibri VFxx # tftp ${loadaddr} vybridm4-freertos.itb
Colibri VFxx # ubi write ${loadaddr} m4firmware ${filesize}
Colibri VFxx # run update_rootfs

Once the volume is in place, a new firmware can be written by just using ubi write:

Colibri VFxx # ubi part ubi
Colibri VFxx # ubi write ${loadaddr} m4firmware ${filesize}

Use the following command to enable automatic loading and execution of the firmware

Colibri VFxx # setenv m4Boot 'ubi read ${loadaddr} m4firmware && m4boot ${loadaddr}'
Colibri VFxx # saveenv

Booting from Linux

In our release image (since v2.6.1), by default remoteproc is disabled to allow Cortex-M4 to start from U-Boot. To start Cortex-M4 using remoteproc, deploy the .elf to '/lib/firmware' directory and load the remoteproc kernel modules. To auto load the remoteproc driver during boot, manually add vf610_cm4_rproc.conf file in '/etc/modules-load.d/' with the remoteproc driver vf610_cm4_rproc specified in the conf file.

root@colibri-vf:~# cat /etc/modules-load.d/vf610_cm4_rproc.conf

If you build the image using OpenEmbedded, you can have the configuration to be deployed with the image by adding KERNEL_MODULE_AUTOLOAD in conf/local.conf:

KERNEL_MODULE_AUTOLOAD += "vf610_cm4_rproc"

Note: remoteproc by default looks for freertos-rpmsg.elf binary in /lib/firmware. To load a custom .elf one can create a symlink so that freertos-rpmsg.elf points to the actual .elf.

root@colibri-vf:~# cd /lib/firmware/
root@colibri-vf:~# ln -s hello_world.elf freertos-rpmsg.elf
root@colibri-vf:~# dmesg|grep remoteproc
[    4.897291]  remoteproc0: vf610_m4 is available
[    4.909798]  remoteproc0: Note: remoteproc is still under development and considered experimental.
[    4.934253]  remoteproc0: THE BINARY FORMAT IS NOT YET FINALIZED, and backward compatibility isn't yet guaranteed.
[    4.983452]  remoteproc0: powering up vf610_m4
[    5.069934]  remoteproc0: Booting fw image freertos-rpmsg.elf, size 160544
[    5.084654]  remoteproc0: No resource table found, continuing...
[    5.098450]  remoteproc0: remote processor vf610_m4 is now up


This section provides information about the available examples and how to use them. The source code of the Colibri VF61 examples are located under examples/vf6xx_colibri_m4/. All examples are linked to run in the SRAM area.

Precompiled firmwares can be found at:

Hello World

  • Firmware Location: examples/vf6xx_colibri_m4/demo_apps/hello_world/
  • Compiled Example: hello_world.bin

Simple application starting the FreeRTOS kernel and printing Hello World in a FreeRTOS task.

Hello World!

GPIO Example

  • Firmware Location: examples/vf6xx_colibri_m4/gpio_sample
  • Compiled Example: gpio_sample.bin

GPIO example using input and output. Example uses SO-DIMM_PIN: for button/key and SO-DIMM_PIN: for a LED. On a Colibri Evaluation board, you can use header X9 to connect the SO-DIMM signals to a button or LED respectively:

  • SO-DIMM_PIN:55 => X9-LED1
  • SO-DIMM_PIN:63 => X9-SW6

RPMsg TTY Example

  • Firmware Location: examples/vf6xx_colibri_m4/demo_apps/rpmsg/str_echo/
  • Compiled Example: rpmsg_str_echo_freertos_example.bin
  • Linux Kernel Module Source:: drivers/rpmsg/imx_rpmsg_tty.c

This demo uses a virtual TTY on Linux side to send a RPmsg to FreeRTOS. Each string sent to the FreeRTOS example will be sent back to the Linux host.

Start the example rpmsg_str_echo_example.bin on the Cortex-M4 first, which should already print the following output:

Starting RPMSG String Echo Demo...
RPMSG String Echo Demo...
RPMSG Init as Remote
init M4 as REMOTE

Then, boot into Linux and execute the following commands:

# modprobe imx_rpmsg_tty
[   63.814232] imx_rpmsg_tty rpmsg0: new channel: 0x400 -> 0x1!
[   63.831681] Install rpmsg tty driver!

With that, we have a TTY device located at /dev/ttyRPMSG. The probe method also sends a "hello world" to the other side, which should be visible in the Cortex-M4 console:

Name service handshake is done, M4 has setup a rpmsg channel [1 ---> 1024]
Get Message From A5 : "hello world!" [len : 12] from slot 0

We can use bash to open the file and assign it the file descriptor 3, write into it, read from it and ultimately close it.

# stty -F /dev/ttyRPMSG -echo
# exec 3<> /dev/ttyRPMSG 
# echo Test >&3 
# cat <&3
# exec 3>&-

On the Cortex-M4 side, this should print the Test message:

Get Message From A5 : "Test" [len : 4] from slot 1
Get New Line From A5 From Slot 2

To load the module automatically on every boot, a config file in the modules-load.d directory can be used:

# echo imx_rpmsg_tty >> /etc/modules-load.d/vf610_rpmsg.conf

RPMsg PingPong Example

  • Firmware Location: examples/vf6xx_colibri_m4/demo_apps/rpmsg/pingpong/
  • Compiled Example: rpmsg_pingpong_freertos_example.bin
  • Linux Kernel Module Source:: drivers/rpmsg/imx_rpmsg_pingpong.c

This example uses the messaging mechanism to send an integer value back and forth, while incrementing it on both sites. The demo will stop after 1000 iterations (that is when 2000 incrementations have been executed).

modprobe imx_rpmsg_pingpong 
[   60.853586] imx_rpmsg_pingpong rpmsg0: new channel: 0x400 -> 0x1!
[   60.875905] get 1 (src: 0x1)
[   60.886584] get 3 (src: 0x1)
[   60.893515] get 5 (src: 0x1)
[   60.899517] get 7 (src: 0x1)
[   60.905504] get 9 (src: 0x1)
[   60.912600] get 11 (src: 0x1)
[   60.918611] get 13 (src: 0x1)
[   60.924596] get 15 (src: 0x1)
[   67.384454] get 1999 (src: 0x1)
[   67.387614] imx_rpmsg_pingpong rpmsg0: goodbye!
Starting RPMSG PingPong Demo...
RPMSG PingPong Demo...
RPMSG Init as Remote
init M4 as REMOTE
Name service handshake is done, M4 has setup a rpmsg channel [1 ---> 1024]
Get Data From A5 : 0
Get Data From A5 : 2
Get Data From A5 : 4                                                    
Get Data From A5 : 6                                                    
Get Data From A5 : 8                                                    
Get Data From A5 : 10                                                   
Get Data From A5 : 12                                                   
Get Data From A5 : 14
Get Data From A5 : 16

Use debug level 5 to avoid the messages on the serial console

# echo 5 > /proc/sys/kernel/printk

Support on Windows CE

Demo with prebuilt M4 binary:

  1. Download Toradex CE Library V1.9 or higher(d92045c9-c212-11e2-b9c5-9e9dd95319f8) and build Rpmsg_Demo application.
  2. Run Rpmsg_Demo.exe application along with rpmgsp_pingpong_example.bin
  3. Verify if firmware is running or not by displaying the messages received from M4.

Build M4 binary on Windows 7 machine

  1. Install Git and clone the repository:
git clone -b colibri-vf61-m4-freertos-v8 git://
  1. Download and Install MinGW.

  2. Select mingw32-base and msys-base package and update it.

  3. Install cmake

  4. Download the zip package of GNU ARM Embedded Toolchain and extract its content inside C:\MinGW\bin. You should see the folders arm-none-eabi, bin, lib and share below C:\MinGW\bin".

  5. Set the user environment variables(My Computer > Properties > Advanced System settings > Environment variables). ARMGCC_DIR=C:\MinGW\bin and PATH=C:\MinGW\bin"

  6. Run build_all.bat to build FreeRTOS source code, which you can find in FreeRTOS source code directories.

Extract entry point instruction from firmware:

  1. Run C:\MinGW\msys\1.0\msys.bat file in a command prompt.
  2. Run command readelf -h firmwareFile.elf, it will output entry point address of the firmware file.

Configure the library through API:

  1. Configure LoadAddr, the address firmware need to be loaded.
  2. Configure CodeSize, the Code block size of the firmware.
  3. Configure ExecuteAddr, the entry point instruction of the firmware. Please refer Memory Map section for more details.
  4. Configure RxRingAddr, Rx shared memory address. This must be identical to the "VRING0_BASE" value in the file "\freertos-toradex\middleware\multicore\open-amp\porting\vf6xx_m4\platform_info.c".
  5. Configure TxRingAddr, Tx shared memory address. This must be identical to the "VRING1_BASE" value in the file "\freertos-toradex\middleware\multicore\open-amp\porting\vf6xx_m4\platform_info.c".
  6. Configure ChannelName, communication channel name to filter the packets. This must be identical to the configured channel name in proc_table structure in "\freertos-toradex\middleware\multicore\open-amp\porting\vf6xx_m4\platform_info.c".
  7. Call Rpmsg_Open to initialize the firmware.
  8. Get name of the event using DataAvailableEvent for creating an event and wait till you receive any message.
  9. Flush the message buffer by calling FlushRcvFifo and start transmitting and receiving data.

Contact Support

In case you have any query or feedback, please contact our support(da00418f-27ab-11e1-8146-0021cc5d255c).

See also