Authors: Robert Chyla and Thomas Andersson, IAR Systems In embedded software development, utilizing full application trace gives developers endless possibilities to analyze their product’s behavior. With complete insight into the application, they can track every single instruction and see if their application is running as intended, or if errors or vulnerabilities occur. So, how can you best utilize trace which is now available for RISC-V?
What is Trace?
In contrast to traditional debugging by setting breakpoints, printf, etc., trace is more like observing your application without intruding. Basically, developers can watch the full application at work without disturbing it. Trace can include a full PC flow (no need for printf nor UART) and once trace data has been captured, you can go back to quickly isolate exceptions and hard faults. This makes finding bugs that are rare and dependent on order-of-execution much easier since this provides a trail of what the program has executed but also in what order. This allows the developer to understand exactly how and why they ended up at a particular line of code. You can quickly find exceptions and hard faults, and therefore find bugs which are rare and dependent on the order of execution. Without trace, if you experience a crash of your program it is hard to recreate what actually happened. However, trace is not only about finding bugs. Trace allows for profiling, code coverage, etc., and you can live-stream the behavior of your device. Performance and coverage monitoring are powerful features that are made possible when you have trace available. If you have a broad bandwidth, even live trace streams can be integrated in your debugger.Trace for RISC-V
One of the major focus points of the RISC-V organization is working towards a standardized specification for RISC-V. Working groups, which are open to all RISC_V members, are currently focusing on developing these specifications. One example is the Processor Trace working group, which ratified the processor trace specification this past February. Another example is the Nexus Trace group that is working on recommendations of how to use trace as defined by the Nexus IEEE-ISTO 5001™ standard for RISC-V cores. This work will continue, as all aspects of trace standard have to be considered. Among other things, this includes the trace control export formats. The minimum goal is to get on par with what already exists for more mature architectures. If the RISC-V trace specification is done right, it will enable easy adoption of existing trace viewers, hardware trace probes and trace analysis tools. A few implementations are already available, but the RISC-V architecture should have trace in every device from IoT to servers. Even simple, standard trace is better than no trace. Figure 1. Trace from a RISC-V deviceSeeing Every Instruction
The integrated support for trace in software development tools enhances everyday code development/debugging. Trace should be an inherent part of the designer’s everyday environment and should not be something you analyze afterwards. So you can write your code, make it run and see how you have arrived at your current execution point. You can iterate and get good code quality directly, as you can go back and quickly isolate exceptions and hard faults. You can also find bugs that are dependent on order-of-execution and find power measurements which can be correlated with your program flow. All these analyses can also be done in a multicore environment with its challenging and complex dependencies. Going through a captured trace can be like finding a needle in a haystack when looking for a bug. In just a few seconds of execution time, hundreds of millions of instructions can be produced. Therefore, it is extremely important that the specification will provide enough triggers to be able to limit the capture only to specific areas. Advanced navigation and search capabilities are essential and if your compiler/debugger tools offer that feature, use trace triggers to constrain trace data to only what you need.Why Do I Want Trace?
Implementing trace IP into your device gives you the possibility to non-intrusively track your product as it is running. Even adding low level debug printouts will change the timing of your application and obscure its true behavior. There are many ways to capture and get trace data exported out from a device:- Serial
- Enough for PC sampled trace (good for statistical code profiling)
- Light instrumentation, real-time operating system (RTOS) monitoring, variable tracing etc.
- With a good probe it is still possible to reach speeds up to several megabytes/s
- High speed parallel interfaces (4 to 16-bit dual-edge)
- Capture everything (clock speed can be very high)
- Traces via “breadcrumbs” left when control flow diversion occurs
- Guarantees you every single instruction executed
- Trace breadcrumbs are stored on debugger probe
- RAM Buffer
- Either small dedicated RAM or shared with system memory
- Even 4KB of trace RAM can provide enough to be really useful
- High speed serial
- Speeds of 10 Gbits/s or higher
- Mainly suitable for bigger, complex systems
- Trace over functional interfaces (USB3.0 provides a lot of bandwidth!)
- Use cases are limited – not an option for small IoT devices