How We Found a Bug In The nRF52 SDK Using an Emulator
Those of you who are following this blog might already know that Jumper is developing an emulator for IoT and embedded systems. This post tells the story of how using our emulator, we revealed a bug in Nordic’s official nRF5 SDK v13.0.0 .
We were implementing the PPI, an nRF52 peripheral that connects an event from one peripheral to a task on another. For example, you can use the PPI to connect a rising-edge event from the GPIO to a START task on the timer. In this case, as soon as the GPIO pin level is rising, the timer will start counting. This process will happen in HW without any interrupts or polling by the CPU.
We implemented the PPI and tried to test it with the PPI example from Nordic’s SDK.
THE PPI EXAMPLE
This is how this example works:
- TIMER0 is used in counter mode, meaning it can only be manually incremented. It will only increment while it is started and not while it is stopped.
- TIMER1 is used to generate an event every even number of seconds.
- TIMER2 is used to generate an event every odd number of seconds.
- The PPI is used to connect the events from TIMER1 to a STOP TASK on TIMER0 and the events from TIMER2 to a START TASK on TIMER0.
While the peripherals are doing their thing, the main loop is trying to increment TIMER0 and prints its value:
Since TIMER0 is stopped for one second at a time, the output should and does look like this:
TRYING IT OUT ON THE EMULATOR
While everything was working well on HW, something went wrong when we tried to run it on the emulator:
As you can see, the timer keeps incrementing even when it shouldn’t.
WHAT WENT WRONG?
Well, we found it right away. You see, the emulator has a log file which reports all events/tasks, interrupts, etc. Checking out this log file quickly revealed the issue:
The log makes it easy to see that TIMER2 creates an event every second instead of creating one every odd second. This causes the PPI to create a STOP event and a START event right after, virtually doing nothing at all.
On HW, the START event was generated right before the STOP event, causing it to appear as the START event never happened.
ROOT CAUSE AND BUG FIX
Checking out the initialization of TIMER2, we immediately saw the issue:
The timer is configured to create an event when its counter gets to 0x7FFF (1 second) and overflow when it passes 0xFFFF (2 seconds) so it should generate an event every odd second. The problem was created by the short created between the compare event and the clear task. As soon as a compare event occurs (when the counter reaches 0x7FFF=1sec), a clear task is initiated and resets the counter. We changed the short mask to zero and voilà, TIMER2 created an event every odd second and everything worked as required.
FINAL THOUGHTS AND CONCLUSIONS
Embedded programming is all about telling HW what to do, you tell your Minions (peripherals) what to do and how to interact with each other. Once they’re on their way, you have very little visibility to what they do and whether or not they are doing it right. Peripheral’s events, tasks and internal state are not accessible by a debugger. Debugging and testing an embedded program with an emulator provides an engineer with a view of the peripherals’ internal states.
As I mentioned before, the program was working well on the actual HW before the fix, but keep in mind that this is exactly the problem. One could have used this code example to use the events from TIMER2 for another purpose, maybe connecting it to an ADC sample task or even a radio task, causing a much more significant affect.
SHARE YOUR THOUGHTS
Feel free to share your thoughts and contact us in the comments section or on firstname.lastname@example.org
The development of Jumper’s emulator is currently focused on the nRF52 but other MCUs will be supported soon. The emulator does more than emulating the MCU itself, you can actually connect between real and virtual BLE devices using a computer, connect sensors and more. If you find this interesting — signup to get updates from Jumper and join our closed beta.