In Spring 2012, Infineon introduced the fifth generation of TriCore™. This family line, called AURIX™, was the first to implement up to three 32-bit TriCore™ CPUs and aimed to meet the highest safety standards while significantly increasing performance at the same time. Yet with great architecture comes increased complexity, and if you're a developer about to embark on an AURIX™-based project then you’ll need to know these three programming essentials to get your development job done right the first time.
Diving Into the Design
Pictured below is a flowchart of the sample case that we’ll work on throughout this blog post. Its design is fictitious in as much that it doesn’t reflect anything that might be considered practical. Like with any example, it has been toned down so as to not draw attention away from the main ideas.
Example Flowchart of Functions in an Aurix™-Based Embedded Application
Essential #1 - Synchronizing Startup Codes
The flowchart above begins with the startup code for core tc0, which is the only AURIX™ TriCore™ core that runs after a reset. The first thing it does is initialize the stack pointer and start cores tc1 and tc2, at which point these start executing their respective startup codes as well.
There are now three startup codes running in parallel which simultaneously start processing the system copy table, but only for variables that have been associated to their specific core. Consider the implications of this. Since the amount of variables associated to each core is not necessarily evenly weighed, the startup codes will not finish at the same time. If the ‘winner’ immediately starts with its associated application part then this poses a risk for variables that it shares with other cores.
If these are initialized by one of the other cores, then there’s a risk that they are not yet initialized and the application can potentially break. It is for this reason that within the TASKING® TriCore™ multi-core environment the startup codes wait for each other to finish via a system variable called _tcx_end_c_init.
Essential #2 - Solving the Main Entry Point Conundrum
Here’s a bit of a puzzle: if a single core application has one single program entry point called main, then how many entry points does a multi-core application have? The answer is still one, for the simple reason that there can only be one matching definition for any function declaration. It is for this reason that after startup code synchronization, all startup codes converge into main.
This is the point where your brain might stall for a moment because it’s being trapped into believing that multiple cores are about to do the same thing, which kind of defeats the purpose. Yet the confusion is only momentary when realizing the AURIX™ architecture implements a core special function register that can identify the currently executing core.
Using this feature, the main body can check by which core it is being executed and diverge into tailored code paths defined for those specific cores. Since these can be considered core specific entry points to main, they have been called core0_main, core1_main and core2_main, as observed in the flowchart above.
Essential #3 - Increments, Decrements and States
We’ve now reached the actual functional part of the program consisting of three tasks distributed across three cores. Task core0_main keeps track of the overall state of the application. It is responsible for starting tasks core1_main and core2_main, both of which have been assigned a simple calculous task.
Both tasks walk along circular buffer samples for an equal amount of iterations. However, where one adds a small value to each cell, the other does the opposite using the same amount. While during calculous all cells essentially are in a state of flux, of one thing we can be sure: on completion the aggregate amount added and subtracted to each cell is zero.
To verify this, core0_main prints the contents of the samples buffer when both tasks have signalled that they have completed their work. Each task has its own private circular pointer to access the circular samples buffer. These are represented by pw1 and pw2. Note, however, that access is arbitrated via semaphore sembuf.In chapter 3, we’ll explain its significance.
Implementing These Essentials
This blog post was just a small sampling of how to address some of the multi-core aspects that come into play while working on an AURIX™-based project. If you’re about to embark on your own AURIX™ embedded application then you’ll need the right tools to get the job done. TASKING has just what you need with the release of the TriCore™ VX-toolset which features a powerful palette of language extensions specifically tailored to meet your AURIX™-based development needs.
Learn how to implement your next AURIX™ TriCore™ multi-core design in the TASKING TriCore™ VX-toolset by downloading the free AURIX™ Multi-Core TriCore™ Programming Essentials appnote.