Blog 7 min read

Analyzing FreeRTOS Architecture with CppDepend

Share this article
Analyzing FreeRTOS Architecture with CppDepend

A static code analysis performed using CppDepend provides deep architectural insights into the structural dependencies of the FreeRTOS codebase. By leveraging a Dependency Structure Matrix (DSM), developers can visualize how core kernel components interact with one another.

DSM highlighting the bidirectional dependency cycle between timers.c and tasks.c

Key Architectural Findings

  • Dependency Cycles Identified: The analysis explicitly highlights a circular dependency between core modules, noting that Source/timers.c has a dependency cycle with Source/tasks.c.
  • DSM Mapping: The Dependency Structure Matrix visualizes cross-file couplings across various core components, mapping modules such as Source/event_groups.c, Source/list.c, Source/queue.c, Source/tasks.c, and Source/timers.c.
  • Directory Structure Breakdown: The project layout tracks components under the main freertos namespace, encompassing Source/*, Source/include/*, and target-specific Source/portable/ implementations alongside external dependencies.

Deep Dive: The Dependency Cycle Between timers.c and tasks.c

When drilling down into the Dependency Structure Matrix (DSM), the color-coding reveals the exact nature of this coupling:

  • The Red Cell: Double-clicking the red cell highlights the overarching bidirectional dependency cycle between Source/timers.c and Source/tasks.c.
  • The Green Cells: Represent direct dependencies flowing from functions and structures in tasks.c targeting timers.c.
  • The Blue Cells: Represent the inverse dependencies flowing from timers.c back into tasks.c—such as references to core task controls, scheduler states, and shared structures.

CppDepend Dependency Structure Matrix of the FreeRTOS codebase

Can It Be Improved Architecturally?

From a pure software engineering perspective, decoupling them completely (e.g., making timers a completely independent plug-in module) is technically possible, but it comes with significant trade-offs for embedded systems:

  • Performance & Overhead: FreeRTOS is intentionally designed for microcontrollers with strict RAM and CPU cycle constraints. Decoupling modules typically requires introducing abstraction layers, function pointer tables, or dynamic registration hooks. This adds extra RAM overhead, ROM footprint, and function-call latency.
  • Monolithic Real-Time Design: In safety-critical or resource-constrained RTOS design, tightly controlled, predictable interactions between the core scheduler and auxiliary services (like timers) are preferred over loose plugin architectures to ensure deterministic behavior and easier static analysis (like MISRA compliance).

If you are looking to minimize code size or customize what gets compiled, FreeRTOS allows you to completely exclude timers.c from your CMakeLists.txt if your application doesn't use software timers, leaving only tasks.c, queue.c, and list.c to keep the footprint as lean as possible.

Low Coupling Across FreeRTOS Functions

Using Code Quest (CQLinq) to analyze method coupling across the codebase reveals that almost all FreeRTOS functions maintain exceptionally low coupling.

Code Quest query: only 4 FreeRTOS methods call more than 10 other methods

By querying methods where the number of methods called (NbMethodsCalled) is greater than 10, the results show a remarkably sparse dataset:

from m in Methods
where m.NbMethodsCalled > 10
select new { m, m.NbMethodsCalled }
  • Minimal High-Coupling Outliers: Across the entire freertos solution, only 4 methods meet or exceed this threshold.
  • Core Queue Operations: The only functions crossing this boundary reside within Source/queue.c under __Globals, specifically core routing and synchronization primitives like xQueueGenericSend, xQueueReceive, xQueueSemaphoreTake, and xQueuePeek (ranging from 13 to 14 methods called).
  • Architectural Takeaway: This confirms that the vast majority of FreeRTOS functions are granular, self-contained, and interact with a tightly controlled number of collaborators.

Granular Modularity: Function-Level Architecture in Code City

Visualizing the FreeRTOS codebase through a 3D Code City metaphor offers an intuitive perspective on how functions are structured across the project. Rather than sprawling, monolithic blocks of code, the architectural layout reveals a dense city of compact, highly specialized buildings representing individual functions.

FreeRTOS codebase visualized as a 3D Code City in CppDepend

  • Compact Function Scale: Code City metrics confirm that core routines maintain a tight, manageable footprint. For instance, critical memory management routines like pvPortMalloc(size_t) span just 71 lines of code while achieving an A rating for maintainability.
  • Single Responsibility Principle: The uniformity of building sizes across the city underscores FreeRTOS's design philosophy—each function is isolated to a distinct, focused purpose.
  • Low Complexity and High Maintainability: By keeping individual routines small, complex conditional logic and technical debt are minimized, which is vital for real-time systems.

High Maintainability Across FreeRTOS Source Files

Analyzing the architectural dependency and metric graph reveals that FreeRTOS maintains exceptional maintainability across all core source files and modules.

Code Map of timers.c: blocks sized by LOC and colored by maintainability

  • Uniform "A" Ratings: Every major subsystem component—including Source/queue.c, Source/tasks.c, Source/timers.c, and Source/portable/MemMang/heap_4.c—earns a top-tier A rating for maintainability.
  • Zero Technical Debt: Despite handling critical RTOS operations, files like Source/queue.c (624 LOC) and Source/tasks.c (1036 LOC) register 0 min 0s of technical debt, proving clean adherence to coding standards.
  • High Maintainability Index (MI): The maintainability index remains consistently high across files, ranging from comfortable scores in heap_4.c (MI: 66) up to excellent ratings in Source/timers.c (MI: 76) and portable helper headers (MI: 78).

Code Map Insight: Inside timers.c

When examining a specific file like timers.c through the Code Map metric visualization (where blocks are sized by lines of code and colored by maintainability), we gain a granular view of how individual routines contribute to the file's cleanliness:

FreeRTOS dependency graph in CppDepend

  • Consistent Green Coloring: The vast majority of functions within timers.c are shaded in a vibrant green, indicating a high maintainability index and low structural complexity.
  • Balanced Method Sizing: While larger utility blocks like prvProcessReceivedCommands take up a noticeable portion of the layout, they are cleanly partitioned alongside smaller helper functions.
  • Clean Architectural Distribution: This visual breakdown confirms that even within files containing complex real-time synchronization logic, functions remain compact, well-defined, and highly maintainable.

Here are a few additional high-level FreeRTOS design and architectural patterns:

1. The Portability Layer (Clean Separation of Concerns)

  • Hardware Abstraction: FreeRTOS cleanly isolates generic scheduling logic from processor-specific details.
  • The portable/ Boundary: All hardware-dependent code—such as context switching, interrupt handling, and SysTick configuration—is sequestered inside the Source/portable/ directory.
  • Architectural Benefit: This modular design allows the core kernel (tasks.c, queue.c) to remain 100% portable across dozens of architectures (ARM Cortex-M, RISC-V, etc.).

2. Opaque Pointers and Data Encapsulation

  • Information Hiding: FreeRTOS heavily utilizes opaque pointers and handle types (like TaskHandle_t or QueueHandle_t) rather than exposing raw control block structures.
  • Architectural Benefit: Application code interacts with the kernel strictly through well-defined APIs. This prevents tight coupling between user tasks and internal kernel states.

3. Plug-and-Play Memory Management Strategies

  • Multiple Heap Options: FreeRTOS provides five distinct memory management implementations (heap_1.c through heap_5.c), ranging from simple non-freeing allocators to more advanced coalescing algorithms.
  • Architectural Benefit: This gives embedded developers architectural control over memory behavior. For safety-critical systems (like automotive or aerospace), developers can select deterministic, fragmentation-free allocators.

4. Low Cyclomatic Complexity for Real-Time Determinism

  • Bounded Execution Paths: Because individual functions are small and maintain low branching complexity (fewer nested if-else statements and loops), the execution time of kernel routines remains highly predictable.
  • Architectural Benefit: In hard real-time operating systems (RTOS), predictable execution time (determinism) is just as important as correctness. Low complexity guarantees the scheduler can meet strict timing deadlines.

Conclusion: Lessons from Analyzing FreeRTOS

The architectural analysis using CppDepend provides clear insights into how FreeRTOS balances real-time performance requirements with clean code practices:

  • Pragmatic Coupling Over Absolute Decoupling: While a dependency cycle exists between Source/timers.c and Source/tasks.c due to shared state like xTimerTaskHandle, this design choice minimizes abstraction overhead, function pointer indirection, and memory footprint—critical priorities for resource-constrained microcontrollers.
  • Exceptional Modularity & Low Coupling: Beyond this single tight interaction, the codebase demonstrates remarkably low coupling. Only four functions across the entire repository call more than ten other methods, reserving complexity strictly for core queue and synchronization primitives in Source/queue.c.
  • High Maintainability & Zero Technical Debt: Through granular, single-responsibility functions, FreeRTOS achieves top-tier A maintainability ratings and 0s technical debt across key files like tasks.c, timers.c, and heap_4.c.

Want to see the architecture of your own firmware? Download CppDepend and run the analysis on your codebase in minutes. You can also read how we visualized another project in From AI-Generated Code to a Living Code City.

Share this article