This page documents a FreeRTOS demo application for the Xilinx Zynq-7000 SoC, which incorporates a dual core ARM Cortex-A9 processor.
The demo is pre-configured to build with the Xilinx SDK tools (version 2016.1 at the time of writing) and execute on the ZC702 evaluation board.
The project uses the default hardware design and board support package (BSP) shipped with the SDK, and builds FreeRTOS and lwIP as part of the application (rather than part of the BSP).
The directory structure used by the demo application is shown and described
below. The root CORTEX_A9_Zynq_ZC702 directory is itself
located in FreeRTOS/Demo.
CORTEX_A9_Zynq_ZC702
|
+-RTOSDemo Contains the SDK project and C files specific to the demo.
|
+-RTOSDemo_bsp Contains the hardware BSP.
|
+-ZC702_hw_platform The hardware description.
Notes relating to the directory structure:
main_blinky() creates a very simple demo that includes two tasks and one queue. One task repeatedly sends the value 100 to the other task through the queue. The receiving task toggles an LED each time it receives the message. The message is sent every 200 milliseconds, so the LED toggles every 200 milliseconds.
main_full() creates a comprehensive test and demo application
that demonstrates:
Most of the 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.
The following tasks are created in addition to the standard demo tasks:
These two tasks test the RTOS kernel context switch mechanism by first filling each Cortex-A9 register (including the floating point registers) with a known and unique value, then repeatedly checking that the value originally written to the register is maintained in the register, for the lifetime of the task. The tasks execute at the lowest possible priority (the idle priority), so are preempted frequently. The nature of these tasks necessitates that they are written in assembly.
Two timers are used to test FreeRTOS queues being used from interrupts that nest to a depth of 3 (including the RTOS tick interrupt). A third timer is configured to generate a 20KHz interrupt at a priority above the maximum system call interrupt priority (the maximum system call interrupt priority is explained on the "Running FreeRTOS on a Cortex-A9" page) - giving a total tested interrupt nesting depth of 4.
The high frequency timer is also used as a convenient time source for the collection of run-time statistics. The collected statistics can be viewed using the CLI.
The check task periodically queries the standard demo tasks and the register test tasks to ensure they are functioning as intended. The check task also toggles an LED to give a visual indication of the system status. If the LED toggles every 3 seconds then the check task has not discovered any problems with the executing demo. If the LED toggles every 200 milliseconds then the check task has discovered a problem in at least one task..
The lwIP example can be configured to use either a static or dynamic IP address:
When connected correctly the demo uses the lwIP sockets API to create a FreeRTOS+CLI command console, and the lwIP raw API to create a basic HTTP web server. Server side includes (SSI) are used to generate dynamic data in the served web pages. To connect to the http server simply type the IP address of the target into the address bar of a web browser.
To connect to FreeRTOS+CLI, open a command prompt and enter "telnet <ipaddr>" where <ipaddr> is the IP address of the target. Once connected type "help" to see a list of registered commands. Note this example does not implement a real telnet server, it just uses the telnet port number to allow easy connection using telnet tools.
Hardware setup
The demo uses the default hardware configuration.
All of the 5-way boot selection switches must be switched to the right hand side when the board is viewed with the integrated Digilent JTAG module on the lower edge. This enables booting via JTAG.
The ZC702_hw_platform and RTOSDemo_bsp projects are dependencies of the RTOSDemo project, so only the RTOSDemo project needs to be built explicitly.
All the other tabs in the 'Debug Configurations' dialogue can be left
with their default settings.
Configuration items specific to this demo are contained in /FreeRTOS/Demo/CORTEX_A9_Zynq_ZC702/RTOSDemo/src/FreeRTOSConfig.h. The constants defined in this file can be edited to suit your application.
The vector table defined in FreeRTOS_asm_vectors.S is placed in a linker segment called .freertos_vectors, and the linker script lscript.ld places the .freertos_vectors segment at the beginning of the .text region.
The Xilinx drivers require interrupt service routines (ISRs) to accept a void * parameter, although the parameter is not always used. The required ISR prototype is therefore:
void Interrupt_Handler( void *pvUnusedParameter );
The interrupt handler called prvUART_Handler() in serial.c
provides an example of an interrupt handler that does not use its parameter. The
interrupt handler called prvTimerHandler() in IntQueueTimer.c
provides an example of an interrupt that uses its parameter to determine which
peripheral generated the interrupt, as in that case the same interrupt handler
implementation is installed as the handler for more than one timer.
If an ISR causes a task of equal or higher priority than the currently executing task to leave the Blocked state then the ISR must request a context switch before the ISR exits. When this is done the interrupt will interrupt one RTOS task, but return to a different RTOS task.
The macros portYIELD_FROM_ISR() (or portEND_SWITCHING_ISR()) can be used to request a context switch from within an ISR. The following source code snippet is provided as an example. The example ISR uses a semaphore to synchronise with a task (not shown), and calls portYIELD_FROM_ISR() to ensure the interrupt returns directly to the task. The prvUART_Handler() and prvTimerhandler() functions already referenced provide further examples.
void Dummy_IRQHandler( void *pvUnusedInThisExample )
{
long lHigherPriorityTaskWoken = pdFALSE;
/* The parameter is not used in this case. */
( void ) pvUnusedInThisExample;
/* Clear the interrupt if necessary. */
Dummy_ClearITPendingBit();
/* This interrupt does nothing more than demonstrate how to synchronise a
task with an interrupt. A semaphore is used for this purpose. Note
lHigherPriorityTaskWoken is initialised to pdFALSE. */
xSemaphoreGiveFromISR( xTestSemaphore, &lHigherPriorityTaskWoken );
/* If there was a task that was blocked on the semaphore, and giving the
semaphore caused the task to unblock, 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 xSemaphoreGiveFromISR(). 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 );
}
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 (meaning a numerically higher value).