Inquire: Call 0086-755-23203480, or reach out via the form below/your sales contact to discuss our design, manufacturing, and assembly capabilities.
Quote: Email your PCB files to Sales@pcbsync.com (Preferred for large files) or submit online. We will contact you promptly. Please ensure your email is correct.
Notes: For PCB fabrication, we require PCB design file in Gerber RS-274X format (most preferred), *.PCB/DDB (Protel, inform your program version) format or *.BRD (Eagle) format. For PCB assembly, we require PCB design file in above mentioned format, drilling file and BOM. Click to download BOM template To avoid file missing, please include all files into one folder and compress it into .zip or .rar format.
After spending years developing firmware for Zynq-based systems, I’ve learned that choosing the right software approach can make or break a project. Whether you need the raw speed of bare-metal code, the task management of Zynq FreeRTOS, or the determinism of Zynq VxWorks, understanding your options matters. This guide walks through Zynq programming from the ground up, covering everything from simple register access to full Zynq RTOS implementations.
The Zynq-7000 SoC combines dual ARM Cortex-A9 processors with programmable logic, creating a unique development challenge. Unlike traditional microcontrollers where you pick one approach and stick with it, Zynq programming offers multiple paths depending on your application requirements.
Three Approaches to Zynq Software Development
Approach
Complexity
Boot Time
Real-Time Capability
Use Case
Bare-Metal
Low
Fast (<100ms)
Excellent (no OS overhead)
Simple control loops, fast prototyping
Zynq RTOS
Medium
Medium (100-500ms)
Good to Excellent
Multi-task applications, networking
Linux
High
Slow (5-30s)
Limited
Complex applications, UI, networking
Each approach has its place. I’ve used bare-metal for motor control applications where every microsecond counts, Zynq FreeRTOS for industrial controllers with multiple concurrent tasks, and Linux for systems requiring extensive networking and user interfaces. The key is matching the approach to your requirements.
Getting Started with Bare-Metal Zynq Programming
Bare-metal programming means your code runs directly on the hardware without any operating system layer. It’s the most straightforward approach and where most engineers start their Zynq programming journey.
Setting Up the Vitis Development Environment
AMD’s Vitis IDE (which replaced SDK) handles bare-metal development. After exporting your hardware design from Vivado as an XSA file, you create a platform project that generates the Board Support Package (BSP).
The BSP contains drivers for all peripherals in your design. For a typical Zynq-7000 system, you get drivers for:
Peripheral
Driver
Header File
GPIO (PS)
xgpiops
xgpiops.h
GPIO (AXI)
xgpio
xgpio.h
UART
xuartps
xuartps.h
Timer
xscutimer
xscutimer.h
Interrupt Controller
xscugic
xscugic.h
SPI
xspips
xspips.h
I2C
xiicps
xiicps.h
Ethernet
xemacps
xemacps.h
Writing Your First Bare-Metal Application
A basic bare-metal application follows a predictable structure. Here’s what a typical GPIO control application looks like:
Implementing Interrupts in Bare-Metal Applications
Most real applications need interrupts. The Zynq-7000 uses a Generic Interrupt Controller (GIC) that handles both private peripheral interrupts (PPI) and shared peripheral interrupts (SPI).
The Zynq-7000’s dual Cortex-A9 cores can run independent bare-metal applications. CPU0 boots first and can wake CPU1 using the System Level Control Register (SLCR). This asymmetric multiprocessing (AMP) configuration works well for applications requiring hard real-time on one core while the other handles less critical tasks.
Key considerations for dual-core bare-metal:
Aspect
CPU0
CPU1
Boot Sequence
Boots first from FSBL
Woken by CPU0
Memory
Full DDR access
Must avoid CPU0 regions
Interrupts
Configure GIC first
Uses GIC configured by CPU0
Cache
Enable independently
Enable independently
Communication
Shared OCM or DDR
Shared OCM or DDR
The On-Chip Memory (OCM) is perfect for inter-processor communication because both cores can access it without cache coherency issues.
Migrating to Zynq FreeRTOS
When your bare-metal application grows beyond a simple control loop, managing multiple tasks becomes challenging. That’s where Zynq FreeRTOS comes in. It’s an open-source real-time operating system that adds task scheduling, inter-task communication, and timing services while keeping overhead minimal.
Why Choose Zynq FreeRTOS
FreeRTOS offers several advantages over bare-metal:
Feature
Bare-Metal
Zynq FreeRTOS
Task Management
Manual (state machines)
Built-in scheduler
Timing Services
Polling or interrupts
vTaskDelay, software timers
Inter-task Communication
Global variables
Queues, semaphores, mutexes
Memory Management
Static allocation
Dynamic or static allocation
Priority Handling
Manual implementation
Priority-based preemption
Code Portability
Platform-specific
Portable API
The learning curve is manageable, and the benefits become apparent as soon as you have more than two or three concurrent activities to manage.
Creating a Zynq FreeRTOS Project
In Vitis, creating a FreeRTOS project is straightforward. When creating a new platform project, select “freertos10_xilinx” as the operating system instead of “standalone.”
The platform generates a FreeRTOS BSP that includes:
FreeRTOS kernel source files
Zynq-specific port layer
Xilinx driver libraries
Configuration header (FreeRTOSConfig.h)
FreeRTOS Task Structure for Zynq
Every FreeRTOS task follows the same basic structure. Tasks run in an infinite loop and should never return:
#include “FreeRTOS.h”
#include “task.h”
void vLEDTask(void *pvParameters)
{
// Task initialization
XGpioPs_SetDirectionPin(&Gpio, LED_PIN, 1);
XGpioPs_SetOutputEnablePin(&Gpio, LED_PIN, 1);
// Task loop – runs forever
for(;;)
{
XGpioPs_WritePin(&Gpio, LED_PIN, 1);
vTaskDelay(pdMS_TO_TICKS(500));
XGpioPs_WritePin(&Gpio, LED_PIN, 0);
vTaskDelay(pdMS_TO_TICKS(500));
}
}
int main(void)
{
// Create the LED task
xTaskCreate(vLEDTask,
“LED”,
configMINIMAL_STACK_SIZE,
NULL,
tskIDLE_PRIORITY + 1,
NULL);
// Start the scheduler
vTaskStartScheduler();
// Should never reach here
for(;;);
}
The vTaskDelay() function is crucial because it yields CPU time to other tasks instead of busy-waiting.
Inter-Task Communication with Queues
Queues provide thread-safe data passing between tasks. They’re essential for producer-consumer patterns common in embedded systems:
Many Zynq applications need networking. The lightweight IP (lwIP) stack integrates well with FreeRTOS and supports TCP/IP, UDP, DHCP, and more.
AMD provides example projects that demonstrate FreeRTOS with lwIP:
Example Application
Description
freertos_lwip_echo_server
TCP echo server for testing
freertos_lwip_tcp_perf_client
TCP performance testing client
freertos_lwip_tcp_perf_server
TCP performance testing server
freertos_lwip_udp_perf_client
UDP performance testing client
These examples provide excellent starting points for networked applications.
Commercial Zynq RTOS Options: VxWorks
While FreeRTOS handles many applications, some industries require certified, commercially supported operating systems. Zynq VxWorks from Wind River is the industry standard for safety-critical and mission-critical applications.
When to Consider Zynq VxWorks
Requirement
FreeRTOS
Zynq VxWorks
Safety Certification (DO-178C, IEC 62304)
Not certified
VxWorks Cert available
Commercial Support
Community + AWS
Full Wind River support
Long-term Maintenance
Open source updates
Guaranteed lifecycle
Deterministic Scheduling
Good
Excellent
Memory Protection
Limited
Full MMU support
POSIX Compatibility
Via wrapper
Native support
License Cost
Free (MIT)
Commercial license required
Aerospace, defense, medical devices, and industrial safety systems often mandate Zynq VxWorks or similar certified RTOS solutions.
VxWorks Architecture on Zynq-7000
VxWorks uses the same boot sequence as other Zynq software: FSBL loads the VxWorks bootloader, which then loads the VxWorks kernel. The VxWorks BSP for Zynq-7000 includes:
Component
Function
Board Support Package
Hardware abstraction layer
VxWorks Kernel
Real-time scheduler, memory management
Device Drivers
UART, Ethernet, SPI, I2C, GPIO
Network Stack
Full TCP/IP, IPv6 support
File Systems
dosFs, HRFS, NFS client
Development Tools
Wind River Workbench IDE
Setting Up Zynq VxWorks Development
VxWorks development requires Wind River Workbench and a valid license. The workflow follows these steps:
Create a VxWorks Source Build (VSB) project
Configure the VSB for Zynq-7000 ARM Cortex-A9
Build the VxWorks Image Project (VIP)
Configure bootloader (typically U-Boot)
Deploy to target via SD card, QSPI, or TFTP
The cross-compilation toolchain compiles your application code on a development host, producing ARM binaries for the Zynq target.
VxWorks Real-Time Performance
VxWorks excels at deterministic performance. Interrupt latency and task switching times are guaranteed and documented:
Metric
Typical Value (Zynq-7000 @ 667MHz)
Interrupt Latency
<1 µs
Task Switch Time
<2 µs
Semaphore Give/Take
<1 µs
Message Queue Send
<2 µs
These numbers make Zynq VxWorks suitable for hard real-time applications where missing a deadline causes system failure.
Asymmetric Multiprocessing: Mixing Operating Systems
One of Zynq’s most powerful capabilities is running different operating systems on each ARM core. You might run Linux on CPU0 for networking and user interface while CPU1 runs Zynq FreeRTOS or bare-metal code for real-time control.
OpenAMP Framework for Mixed OS
AMD supports the OpenAMP framework, which provides:
Feature
Purpose
Remoteproc
Life cycle management (load, start, stop)
RPMsg
Inter-processor messaging
VirtIO
Virtualized I/O queues
Linux acts as the master, managing the remote processor running FreeRTOS or bare-metal firmware. The remote processor can be loaded dynamically without rebooting the system.
Memory Partitioning for AMP
When running multiple operating systems, careful memory partitioning prevents conflicts:
Memory Region
Size
Owner
0x00000000 – 0x3FFFFFFF
1 GB
Linux (DDR)
0x3FF00000 – 0x3FFFFFFF
1 MB
Shared (RPMsg buffers)
0x3FE00000 – 0x3FEFFFFF
1 MB
FreeRTOS code/data
0xFFFC0000 – 0xFFFFFFFF
256 KB
OCM (shared)
The device tree for Linux must be configured to avoid the regions reserved for the remote processor.
Debugging Zynq RTOS Applications
Debugging RTOS applications requires different techniques than bare-metal debugging. Task states, queue contents, and timing relationships all matter.
JTAG Debugging with Vitis
Vitis provides RTOS-aware debugging for FreeRTOS:
Debug Feature
Description
Task List
View all tasks, states, priorities
Queue Browser
Inspect queue contents
Semaphore Status
See semaphore owners and waiters
Stack Usage
Monitor task stack consumption
CPU Load
Task-level CPU utilization
Set breakpoints in task code, and the debugger shows which task hit the breakpoint along with the full call stack.
Using FreeRTOS Trace Hooks
FreeRTOS includes trace hook macros that third-party tools like Percepio Tracealyzer can capture. These provide non-intrusive insight into system behavior:
// In FreeRTOSConfig.h
#define configUSE_TRACE_FACILITY 1
#define configGENERATE_RUN_TIME_STATS 1
The trace output shows task execution patterns, timing, and resource usage graphically.
Essential Resources for Zynq Programming
Having the right documentation and tools makes Zynq programming significantly easier. Here are the resources I keep bookmarked:
FreeRTOS Community Forums: https://forums.freertos.org/
Frequently Asked Questions About Zynq Programming
What is the difference between bare-metal and RTOS programming on Zynq?
Bare-metal programming runs your code directly on the hardware without any operating system. You get maximum performance and minimal overhead, but you must manage everything yourself including timing, task switching, and resource sharing. Zynq RTOS programming adds a kernel layer that handles task scheduling, timing services, and inter-task communication. The RTOS takes care of context switching and priority management, letting you focus on application logic. For simple applications with one or two activities, bare-metal works fine. Once you have multiple concurrent tasks with different priorities and timing requirements, an RTOS dramatically simplifies development.
Can I run Zynq FreeRTOS on both ARM cores simultaneously?
Yes, but it’s not the typical configuration. The more common approach is running FreeRTOS on one core while the other runs bare-metal code or Linux. If you need FreeRTOS on both cores, you would run two separate FreeRTOS instances in an AMP configuration, not a single SMP instance. Each core runs its own scheduler with its own tasks. Communication between cores uses shared memory regions, typically the On-Chip Memory (OCM). AMD’s OpenAMP framework provides standardized mechanisms for this inter-processor communication. For true SMP FreeRTOS (single scheduler managing both cores), the standard Xilinx FreeRTOS port doesn’t support this out of the box.
How do I choose between Zynq FreeRTOS and Zynq VxWorks?
The decision usually comes down to three factors: certification requirements, budget, and support needs. If your application requires safety certification (aerospace DO-178C, medical IEC 62304, industrial IEC 61508), VxWorks Cert provides pre-certified components that dramatically reduce certification effort. FreeRTOS lacks these certifications. Budget matters because VxWorks requires commercial licensing while FreeRTOS is free under the MIT license. For startups or cost-sensitive projects, FreeRTOS is attractive. Finally, consider support requirements. VxWorks includes commercial support from Wind River with guaranteed response times and long-term maintenance commitments. FreeRTOS relies on community support and AWS stewardship. For mission-critical applications in regulated industries, Zynq VxWorks is usually the right choice despite higher cost.
What timer does FreeRTOS use for the tick on Zynq-7000?
FreeRTOS on Zynq-7000 typically uses the ARM Cortex-A9 private timer (also called the SCU timer) for the tick interrupt. This timer is part of the Snoop Control Unit and runs at half the CPU frequency. The Xilinx FreeRTOS port configures this timer automatically based on the configCPU_CLOCK_HZ and configTICK_RATE_HZ settings in FreeRTOSConfig.h. You can also use the Triple Timer Counter (TTC) if you need the private timer for other purposes, but this requires modifying the port layer. The tick rate is commonly set to 1000 Hz (1ms ticks), providing a good balance between timing resolution and overhead.
How do I share peripherals between tasks in Zynq FreeRTOS?
Peripheral sharing requires protection to prevent concurrent access corruption. FreeRTOS provides mutexes (mutual exclusion semaphores) for this purpose. Before accessing a shared peripheral like UART or SPI, a task takes the mutex. After completing the access, it gives the mutex back. Other tasks attempting to use the peripheral block until the mutex becomes available. For simple cases, binary semaphores work, but mutexes are preferred because they include priority inheritance, which prevents priority inversion problems. Here’s the pattern: create a mutex during initialization, then bracket each peripheral access with xSemaphoreTake() and xSemaphoreGive() calls. For interrupt-driven peripherals, use task notifications or queues to signal the task from the ISR rather than blocking in the interrupt handler.
Performance Optimization Tips for Zynq RTOS
Getting your Zynq RTOS application running is one thing. Getting it running efficiently is another. Here are techniques I’ve found effective across many projects.
Stack Size Optimization
Each FreeRTOS task needs its own stack, and over-allocating wastes precious memory. Use the uxTaskGetStackHighWaterMark() function to measure actual stack usage during development:
Set your stack sizes to about 20-30% above the measured high water mark to provide safety margin without wasting memory.
Interrupt Priority Configuration
Zynq’s GIC supports priority levels for interrupts. Keep high-frequency interrupts at higher priority than lower-frequency ones. Also ensure that any interrupt calling FreeRTOS API functions uses priority levels at or below configMAX_SYSCALL_INTERRUPT_PRIORITY.
Cache Configuration for Real-Time Performance
The Cortex-A9’s L1 and L2 caches dramatically improve performance but can introduce timing variability. For hard real-time code sections, consider using the OCM which doesn’t require cache management. For cached memory regions used with DMA, use Xil_DCacheFlushRange() and Xil_DCacheInvalidateRange() appropriately.
Choosing Your Zynq Programming Path
After working through numerous Zynq projects, I’ve developed a simple decision framework. Start with bare-metal for initial hardware bring-up and simple applications. Move to Zynq FreeRTOS when you need multiple concurrent tasks, timing services, or networking. Consider Zynq VxWorks when certification, long-term support, or hard real-time guarantees are required.
The beauty of Zynq programming is that you’re not locked into one approach. You can run bare-metal on CPU1 for real-time control while CPU0 runs Linux for user interface and networking. Or run FreeRTOS on one core handling sensors while VxWorks handles safety-critical functions on the other.
The key is understanding what each approach offers and matching it to your application requirements. With the tools and techniques covered in this guide, you have the foundation to make that decision and implement it successfully.
Inquire: Call 0086-755-23203480, or reach out via the form below/your sales contact to discuss our design, manufacturing, and assembly capabilities.
Quote: Email your PCB files to Sales@pcbsync.com (Preferred for large files) or submit online. We will contact you promptly. Please ensure your email is correct.
Notes: For PCB fabrication, we require PCB design file in Gerber RS-274X format (most preferred), *.PCB/DDB (Protel, inform your program version) format or *.BRD (Eagle) format. For PCB assembly, we require PCB design file in above mentioned format, drilling file and BOM. Click to download BOM template To avoid file missing, please include all files into one folder and compress it into .zip or .rar format.