|
|
FreeRTOS on the Intel® Galileo
(x86 Quark™ SoC X1000)
[RTOS Ports]
Introduction
The FreeRTOS Eclipse plug-in showing
the state of tasks running on the Galileo
Quark X1000 SoC. Click to enlarge.
This page documents an RTOS demo application that uses the FreeRTOS IA32 (x86)
flat memory model port, and is pre-configured to run on the Galileo Gen 2 single board computer,
which uses an
Intel Quark SoC X1000
(x86/IA32) CPU.
The FreeRTOS IA32 port implements a full interrupt nesting model, utilises a separate
system stack to save RAM, and never globally
disables interrupts (although the hardware itself disables interrupts on interrupt entry).
IMPORTANT! Notes on using the FreeRTOS Intel Quark SoC demo
Please read all the following points before using this RTOS port.
-
Source Code Organisation
-
The RTOS Demo Application
-
Build Instructions
-
Running the RTOS Demo on the Galileo Gen 2 Board
-
Debugging the RTOS Demo on the Galileo Gen 2 Board
-
RTOS Configuration and Usage Details
Also see the FAQ My application does not run, what could be wrong?.
Source Code Organisation
Only a small subset of the files in the FreeRTOS .zip file download are are
required by the Intel Quark SoC demo. The Source Code Organization page describes
the structure of the FreeRTOS zip file download.
The Eclipse project used to build and debug the demo is located in the
FreeRTOS/Demo/IA32_flat_GCC_Galileo_Gen_2
directory. Note the RTOS project includes files from elsewhere in the directory
structure, so the project may not build if the directory structure has been altered.
The Intel Quark SoC X1000 Demo Application
Hardware and software set up
The RTOS demo uses the LED and UART built onto the Galileo hardware, so no
specific hardware setup is required.
The UART uses 115200 baud, no parity bits, and 8 data bits, and one stop bit.
Functionality
The behaviour of the demo is dependant on the mainCREATE_SIMPLE_BLINKY_DEMO_ONLY
constant, which is declared at the top of main.c.
Functionality with mainCREATE_SIMPLE_BLINKY_DEMO_ONLY set to 1
If mainCREATE_SIMPLE_BLINKY_DEMO_ONLY is set to 1 then main() will
call main_blinky().
main_blinky() creates a very simple demo that creates two
tasks and one queue,
as follows:
-
A 'queue send' task
The 'queue send' task sends the value 100 to the queue every 200 milliseconds.
-
A 'queue receive' task
The 'queue receive' task reads values from the queue, toggling an LED
and writing the LED state to the UART, each time the value 100 is received.
If the simple blinky demo is functioning correctly then the LED will toggle every
200 milliseconds.
Functionality with mainCREATE_SIMPLE_BLINKY_DEMO_ONLY set to 0
If mainCREATE_SIMPLE_BLINKY_DEMO_ONLY is set to 0 then main() will
call main_full(). main_full() creates a comprehensive test and demo application
that demonstrates:
Most of the RTOS tasks created by the demo are from the set of standard demo
tasks. These are used by all FreeRTOS demo applications, and
have no specific functionality or purpose other than to demonstrate the FreeRTOS
API being used, and test the RTOS kernel port.
Included in the full demo is a 'check' task. The check task periodically
monitors all the other tasks in the demo before toggling an LED and printing a
status code to the UART. If the LED toggles every five seconds, and the printed
status code is 0, then the check task has not detected any unexpected behaviour.
If the LED toggles every second, then the check task has detected
unexpected behaviour, and the source of the potential error is latched in the
status code value - the meaning of the status code can be determined by inspecting
the implementation of prvCheckTask() within the main_full.c source file.
Build Instructions
The Eclipse project
uses both virtual folders and links that reference files from elsewhere within
the FreeRTOS directory structure. The [virtual] file structure viewed in the
Eclipse project explorer will not match the [actual] file structure
viewed on the disk.
Note the demo was developed and tested on a Windows host. Any issues building the
demo on a Linux host will most likely to be related to the case of letters used
in include file names, or include paths.
Please report any
such errors so they can be corrected.
To build the RTOS demo application:
-
If not already installed, download and install the Eclipse CDT
distribution.
-
If not already installed, download and install an x86 GCC elf compiler.
The project was developed using, and is pre-configured to use, the
pre-built i686-elf-gcc GCC tool chain
referenced from the osdev.org website.
NOTE: It has been discovered that the above
referenced compiler has dependencies on DLLs that are not part of the
compiler package. The simplest way around this is to also
install the
MingW compiler, and ensure the MingW bin directory is also in your path,
as that comes with the necessary dll files. If anybody is aware of an
elf compiler that doesn't have this external dependency then please
let us know.
COFF compilers (such and MingW), can also be used to build the example,
but will probably require
adjustments to the project settings, and will not offer such a good
debugging experience.
-
Ideally, ensure the path to the compiler's bin directory is included in the Windows
PATH environment variable. It is also possible to set the path within the
Eclipse project itself - this is described later.
-
If your host system does not provide an implementation of the 'rm' (remove files)
command, then it may be necessary to provide or install one in order to perform a
'build clean' operation. The UnxUtils
package provides a pre-built version suitable for use on Windows. Ensure
the 'rm' executable is also located in a path that is included in the
Windows PATH environment variable.
-
Open the Eclipse IDE and either create a new workspace or select an existing
workspace when prompted.
-
Select "Import" from the IDE's "File" menu to bring up the import
dialogue box.
-
In the Import dialogue box, select "General->Existing Projects Into Workspace"
then browse to and select the FreeRTOS/Demo/IA32_flat_GCC_Galileo_Gen_2
directory. A project called "RTOSDemo" will be visible.
-
Ensure RTOSDemo is checked, and that Copy Projects Into Workspace is
not checked, before clicking "Finish".
Import RTOSDemo into the Eclipse workspace without
copying it into the workspace.
-
If you are using a GCC compiler other than the i686-elf-gcc distribution,
or you need to set the path to the compiler,
then you can update the Eclipse project's 'cross compiler' settings as
follows:
Select "Properties" from the IDE's "Project" menu to bring up the
properties dialogue box. In the dialogue box select "C/C++Build->Settings->Cross Settings",
then set the name, and if necessary the path, to the
compiler that will be used.
Setting the path to the compiler in the RTOS project's properties dialogue box
-
Open main.c and set mainCREATE_SIMPLE_BLINKY_DEMO_ONLY to generate either
the simple blinky demo, or the full test and demo application, as
required.
-
Select "Build All" from the IDE's "Project" menu. The project should build
with no errors - see the build troubleshooting tips below if this is not the
case.
Build trouble shooting tips:
The RTOS demo executable can be booted on the Galileo board from an SD card.
A multi-boot GRUB image is used for this purpose. The GRUB configuration enables the Galileo to
boot into either FreeRTOS, or Linux. Booting into Linux allows the RTOS demo
executable to be updated over the network.
Full instructions for creating the GRUB image are provided
in the pdf file provided on this link. For
simplicity,
a pre-built SD card image is also provided on this link.
Brief instructions on using the pre-built SD card image are provided below.
Refer to the pdf file for full instructions.
It may be necessary to update the Galileo firmware before proceeding -
instructions for updating the firmware are provided in the same pdf file.
The SD card must be formatted as FAT or FAT32, be 32G bytes or smaller, and SDHC
format. SDXC format is not supported.
To create the initial bootable SD card from a Windows host:
-
Open a command prompt (cmd.exe) as an administrator.
-
Run diskpart.exe in the command prompt to start the diskpart utility.
-
Enter the following diskpart commands:
-
select volume n [where 'n' is the drive
letter assigned to the SD card by windows. For example, if the SD card
was drive z, then you would enter 'select volume z'.]
-
clean
-
create part primary
-
active
-
format quick label="BOOTME"
-
exit
-
Unzip the pre-build SD image
into the root of the partitioned and
formatted SD card, such that the root of the SD card contains the boot
folder, and the bzImage (and other) file.
-
Copy the RTOSDemo.elf file created when the RTOS demo was built into the
/kernel directory on the SD card.
To boot FreeRTOS from the SD card:
-
Connect the Galileo to a host computer running a dumb terminal program
(such as Tera Term) using a suitable serial cable. The connection uses
115200 baud, no parity, 8 data bits and no stop bits.
-
After following the instructions above to create the SD card, insert the
SD card into the slot on Galileo Gen 2 hardware.
-
Reset the Galileo board.
-
The boot options menu will be displayed in the dumb terminal. Select "Multiboot
GRUB", or simply let the menu time out as Multiboot GRUB is the default selection.
The boot options menu
-
The Multiboot GRUB menu will then be displayed in the terminal. Select "FreeRTOS Demonstration",
or simply let the menu time out as FreeRTOS Demonstration is the default selection.
The RTOS demo will start to execute.
The multiboot GRUB menu
Subsequent updates to the RTOS demo elf file can be copied onto the SD card by booting
the Galileo into Linux, then using WinSCP. As supplied, the SD card used to boot FreeRTOS
on the Galileo board will use the 192.168.0.10
IP address, so the host computer will need to have a compatible IP address on the same local
network.
To update the RTOS demo elf file using SCP:
-
If not already installed, download and install WinSCP,
or other compatible SCP client.
-
Reboot the Intel Galileo hardware, this time use the multiboot GRUB menu
to select "Clanton SVP kernel", which will boot Linux instead of FreeRTOS.
-
Wait for Linux to boot, entering "root" as the username and "intel" as the
password when prompted.
-
Ensure the Galileo board is connected to the same network as the host
computer before starting WinSCP.
-
Create then connect a WinSCP session using the settings shown in the image below.
The password is 'intel'.
The necessary WinSCP session settings
-
Once WinSCP has connected, copy the new RTOS demo elf file into the "/media/realroot/kernel"
directory, as shown in the image below.
The directory into which updated FreeRTOS images must be copied
-
Enter "reboot" in the Linux command console. That will ensure the updated
elf file is committed to the SD card before rebooting the Galileo into FreeRTOS.
Eclipse, OpenOCD and GDB can be used to download, run, and debug updated executables, without
needing to reboot the hardware. A JTAG debug interface is required. The demo was created and tested using an
ARM-USB-OCD-H
pod from Olimex, with an ARM-JTAG-20-10
adapter.
These instructions download an image to RAM only, so the updated image will
not be persistent, and will not be available after the Galileo has been reset.
To download a new RTOS demo executable to RAM, then start a debug session:
-
If not already installed, download and install OpenOCD.
-
Connect the Galileo Gen 2 evaluation board to the host computer using a
suitable FTDI based JTAG adapter.
-
Open a command console, navigate to the directory in which openocd.exe
was installed, and execute the following command to start OpenOCD with
the required configuration. Note the command should be entered on a
single line, and assumes the ARM-USB-OCD-H interface is being used.
openocd.exe -f ..\scripts\interface\ftdi\olimex-arm-usb-ocd-h.cfg
-f ..\scripts\board\quark_x10xx_board.cfg
-
Boot the Galileo into FreeRTOS, as described above.
-
In Eclipse, use the "Run->Debug Configurations..." menu item to create
a new debug configuration, as shown in the images below.
Click the images to enlarge
Creating a debug configuration - step 1
Creating a debug configuration - step 2
Creating a debug configuration - step 3. Options that are
not visible in the image are left empty
-
Click "Debug" to connect to the Galileo using GDB. See the debug troubleshooting
tips below.
-
Multiple consoles are available within the Eclipse IDE. Select the GDB console
as shown below:
Selecting the GDB console in Eclipse
-
In the GDB console, enter the following commands, replacing [enter path]
with the correct path to the RTOS demo elf file for your installation.
See the debug troubleshooting tips below. The following commands can be
added to a script for simplicity.
monitor reg eflags 0x0
flushregs
echo Downloading elf file. Please wait.
monitor load_image C:[enter path]/RTOSDemo.elf 0
symbol-file C:[enter path]/RTOSDemo.elf
set $eip=_restart
c
The RTOS demo elf file should download to RAM, which takes some time,
and then start executing.
Debug trouble shooting tips:
-
If the correct source line, or assembly code line, is not displayed when
a debug session is first started, then try a single stepping operation in
the debugger. Sometimes that will result in a refresh, and then the
correct code will be shown.
-
Keep an eye on the console window in which OpenOCD is running. If it
starts to show "invalid memory" errors then it may be necessary to reset
everything, and restart OpenOCD.
-
If you are developing by downloading images to RAM, and you want to have
the debugger stop on entry to main, then add "b main" (breakpoint at
main) command before the "c" (continue) command in step 8 of the instructions
above.
-
If you are developing by copying new RTOS images to the SD card, then
rebooting the Galileo, and you want the debugger to stop on entry to main,
then set the #define mainWAIT_FOR_DEBUG_CONNECTION to 1 at the top of the
main.c source file. That will cause the application to sit in a loop
at the top of main(), giving you the opportunity to connect the debugger
before the application starts executing properly. Once the debugger is
connected it can be used to change the value of ulExitResetSpinLoop to
any non-zero value, and in so doing, exit the loop. The loop can also be
exited by pressing a key in the console.
FreeRTOS Intel IA32 run-time model
The FreeRTOS IA32 (x86) port uses a flat 32-bit memory space, [currently] runs
all tasks with full privileges, and does not use the MMU.
Configuration items specific to this demo are contained in FreeRTOS/Demo/IA32_flat_GCC_Galileo_Gen_2/FreeRTOSConfig.h.
The constants defined in this file can be edited to suit your application.
The following Intel IA32 target specific constants are required in addition to the
standard FreeRTOS configuration constants:
-
configISR_STACK_SIZE
FreeRTOS will switch the stack in use to a dedicated interrupt/system
stack on interrupt entry. configISR_STACK_SIZE defines the number of
32-bit values that can be stored on the system stack, and must be large
enough to hold a potentially nested interrupt stack frame.
Using a separate system stack means the stacks allocated to tasks can all
be smaller, as they do not each need to include space for a nested interrupt
stack frame.
Changing this parameter necessitates a complete clean and rebuild
to ensure the assembly files are also re-built.
-
configSUPPORT_FPU
If configSUPPORT_FPU is set to 1 then tasks can opt to have a floating
point context (the floating point registers will be saved as part of
the task context).
Tasks are not created with an FPU context and must not use any FPU instructions
until after they have called vPortTaskUsesFPU().
void vPortTaskUsesFPU( void );
vPortTaskUsesFPU() function prototype
|
If configSUPPORT_FPU is set to 0 then floating point instructions must
never be used.
Changing this parameter necessitates a complete clean and rebuild
to ensure the assembly files are also re-built.
-
configUSE_COMMON_INTERRUPT_ENTRY_POINT
If configUSE_COMMON_INTERRUPT_ENTRY_POINT is set to 0 then all
interrupt service routines need a short assembly code entry point.
The assembly code wraps the interrupt handler in the FreeRTOS
portFREERTOS_INTERRUPT_ENTRY and portFREERTOS_INTERRUPT_EXIT macros, which handle interrupt
entry and exit respectively. See
the Interrupt Service Routines section for an example.
If configUSE_COMMON_INTERRUPT_ENTRY_POINT is set to 1, then interrupt
service routines can also be written as standard C functions, in which
case interrupt entry and exit is handled by common code within the
FreeRTOS port layer. See the Interrupt Service Routines
section for an example.
Writing an interrupt handler that provides its own assembly file
wrapper is slightly more complex than using the common interrupt
entry point, but interrupt entry will be faster and always deterministic.
-
configMAX_API_CALL_INTERRUPT_PRIORITY
The FreeRTOS Quark port implements a full interrupt nesting model.
Interrupts that are assigned a priority at or below
configMAX_API_CALL_INTERRUPT_PRIORITY can call interrupt safe API functions
and will nest.
Interrupts that are assigned a priority above
configMAX_API_CALL_INTERRUPT_PRIORITY cannot call any FreeRTOS API functions,
will nest, and will not be masked by FreeRTOS critical sections (although all
interrupts are briefly masked by the hardware itself on interrupt entry).
FreeRTOS functions that can be called from an interrupt are those that end in
"FromISR". FreeRTOS maintains a separate interrupt safe API to enable
interrupt entry to be shorter and faster, and to enable all API functions to be
simpler and smaller.
User definable interrupt priorities range from 2 (the lowest) to 15 (the
highest).
Interrupt service routines (ISRs) can be written in two different ways, as follows:
-
As standard C functions
This method can only be used if configUSE_COMMON_INTERRUPT_ENTRY_POINT
is set to 1. The interrupt handler must be installed using the
xPortRegisterCInterruptHandler() function.
BaseType_t xPortRegisterCInterruptHandler( ISR_Handler_t pxHandler,
uint32_t ulVectorNumber );
The xPortRegisterCInterruptHandler() function prototype
|
This is the simplest of the two methods, but incurs a slightly longer
interrupt entry time. Interrupts are enabled before the ISR (the C function)
is called.
The example below is a cut-down version of an interrupt used in the
RTOS demo project.
static void vHPETIRQHandler0( void )
{
hpetIO_APIC_EOI = hpetHPET_TIMER0_ISR_VECTOR;
}
xPortRegisterCInterruptHandler( vHPETIRQHandler0, 100 );
Implementing an ISR as a standard C function
|
-
As standard C functions that are called from an assembly code entry point
The assembly code wraps the interrupt handler in the FreeRTOS
provided portFREERTOS_INTERRUPT_ENTRY and portFREERTOS_INTERRUPT_EXIT macros. The
interrupt handler must be installed using the xPortInstallInterruptHandler()
function.
BaseType_t xPortInstallInterruptHandler( ISR_Handler_t pxHandler,
uint32_t ulVectorNumber );
The xPortInstallInterruptHandler() function prototype
|
This method can always be used. It is slightly more complex than
method 1, but benefits from a faster and deterministic interrupt entry time.
The application writer can re-enable interrupt before calling the C portion
of the ISR if desired.
The example below is a cut-down version of an interrupt used in the RTOS
demo project.
void vHPETIRQHandler1( void )
{
hpetIO_APIC_EOI = hpetHPET_TIMER1_ISR_VECTOR;
}
#include "ISR_Support.h"
.align 4
.func vApplicationHPETTimer1Wrapper
.extern vHPETIRQHandler1
vApplicationHPETTimer1Wrapper:
portFREERTOS_INTERRUPT_ENTRY
sti
call vHPETIRQHandler1
portFREERTOS_INTERRUPT_EXIT
.endfunc
extern void vApplicationHPETTimer1Wrapper( void );
xPortInstallInterruptHandler( vApplicationHPETTimer1Wrapper, 100 );
The C function called from the assembly code wrapper
|
If an ISR causes an RTOS task of equal or higher priority than the currently executing
task to leave the Blocked state (see the description of the pxHigherPriorityTaskWoken
parameter in the API documentation for functions such as
xSemaphoreGiveFromISR())
then the ISR must request a context switch before
the ISR exits if it wants the unblocked task to execute immediately. When this
is done, the interrupt will interrupt one RTOS task, but return to a different
RTOS task.
The macro portYIELD_FROM_ISR() (or portEND_SWITCHING_ISR()) is used to
request a context switch from within an ISR.
The following source code snippet is provided as an example. The ISR in the example
uses a task notification to synchronise with a task (not shown), and calls portYIELD_FROM_ISR()
to ensure the interrupt returns directly to the unblocked task.
The application writer may choose not to call portYIELD_FROM_ISR() if
it is known that the interrupt did not necessitate any immediate processing - for
example, if the interrupt was a character arriving, but more characters are
needed before the message being received is complete and ready for processing.
void Dummy_IRQHandler( void )
{
long lHigherPriorityTaskWoken = pdFALSE;
/* Clear the interrupt if necessary. */
Dummy_ClearITPendingBit();
/* This interrupt does nothing more than demonstrate how to synchronise a
task with an interrupt. A task notification is used for this purpose.
Note lHigherPriorityTaskWoken is initialised to pdFALSE. */
vTaskNotifyGiveFromISR( xTaskHandle, &lHigherPriorityTaskWoken );
/* If the notified task was blocked waiting for the notification, and the
unblocked task has a priority higher than or equal to the currently Running
task (the task that this interrupt interrupted), then
lHigherPriorityTaskWoken will have been set to pdTRUE internally within
vTaskNotifyGiveFromISR(). Passing pdTRUE into the portYIELD_FROM_ISR() macro
will result in a context switch being pended to ensure this interrupt returns
directly to the unblocked, higher priority, task. Passing pdFALSE into
portYIELD_FROM_ISR() has no effect. */
portYIELD_FROM_ISR( lHigherPriorityTaskWoken );
}
An example interrupt handler
|
Only FreeRTOS API functions that end in "FromISR" can be called from an
interrupt service routine - and then only if the priority of the interrupt
is less than or equal to that set by the configMAX_API_CALL_INTERRUPT_PRIORITY
configuration constant.
Resources used by FreeRTOS
FreeRTOS uses the local APIC timer, and interrupt vectors 0x20 and 0x21. In
addition, the demo project uses the UART, GPIO, Legacy GPIO, I2C and HPE timers.
Memory allocation
Source/Portable/MemMang/heap_4.c is included in the Intel IA32 demo application project to provide the memory
allocation required by the RTOS kernel.
Please refer to the Memory Management section of the API documentation for
full information.
Miscellaneous
Note that vPortEndScheduler() has not been implemented.
Copyright (C) Amazon Web Services, Inc. or its affiliates. All rights reserved.
|