PDA

View Full Version : Assertions don't work on Teensy 3.0



froeber
05-28-2013, 01:50 AM
Hi, I like to use "assertions" in my programs to help find problems and help make code that is more reliable. But the standard definitions of assertions for the Teensy 3.0 software doesn't work. Even a simple example made by modifying the standard Blink.ino example shows the problem that pops up while linking. Here's the modified code:


/*
Blink
Turns on an LED on for one second, then off for one second, repeatedly.

This example code is in the public domain.
*/

#include "assert.h"

// Pin 13 has an LED connected on most Arduino boards.
// Pin 11 has the LED on Teensy 2.0
// Pin 6 has the LED on Teensy++ 2.0
// Pin 13 has the LED on Teensy 3.0
// give it a name:
int led = 13;

// the setup routine runs once when you press reset:
void setup() {
// initialize the digital pin as an output.
assert(led <15);
pinMode(led, OUTPUT);
}

I added the include of assert.h and an assertion check in setup(). This isn't how I'm really using assertions in my code, it's just an example to show the problem. And the problem is that when I build the program the link stage comes up with a bunch of undefined references:


C:\arduino\arduino-1.0.5\hardware\tools\arm-none-eabi\bin\arm-none-eabi-gcc -Os -Wl,--gc-sections -mcpu=cortex-m4 -mthumb -TC:\arduino\arduino-1.0.5\hardware\teensy\cores\teensy3\mk20dx128.ld -o C:\froeber\Projects\Kuji_lighter\SmartLighter\Ardu ino\Build\Blink.cpp.elf C:\froeber\Projects\Kuji_lighter\SmartLighter\Ardu ino\Build\Blink.cpp.o C:\froeber\Projects\Kuji_lighter\SmartLighter\Ardu ino\Build\analog.c.o C:\froeber\Projects\Kuji_lighter\SmartLighter\Ardu ino\Build\eeprom.c.o C:\froeber\Projects\Kuji_lighter\SmartLighter\Ardu ino\Build\keylayouts.c.o C:\froeber\Projects\Kuji_lighter\SmartLighter\Ardu ino\Build\mk20dx128.c.o C:\froeber\Projects\Kuji_lighter\SmartLighter\Ardu ino\Build\nonstd.c.o C:\froeber\Projects\Kuji_lighter\SmartLighter\Ardu ino\Build\pins_teensy.c.o C:\froeber\Projects\Kuji_lighter\SmartLighter\Ardu ino\Build\serial1.c.o C:\froeber\Projects\Kuji_lighter\SmartLighter\Ardu ino\Build\serial2.c.o C:\froeber\Projects\Kuji_lighter\SmartLighter\Ardu ino\Build\serial3.c.o C:\froeber\Projects\Kuji_lighter\SmartLighter\Ardu ino\Build\touch.c.o C:\froeber\Projects\Kuji_lighter\SmartLighter\Ardu ino\Build\usb_desc.c.o C:\froeber\Projects\Kuji_lighter\SmartLighter\Ardu ino\Build\usb_dev.c.o C:\froeber\Projects\Kuji_lighter\SmartLighter\Ardu ino\Build\usb_joystick.c.o C:\froeber\Projects\Kuji_lighter\SmartLighter\Ardu ino\Build\usb_keyboard.c.o C:\froeber\Projects\Kuji_lighter\SmartLighter\Ardu ino\Build\usb_mem.c.o C:\froeber\Projects\Kuji_lighter\SmartLighter\Ardu ino\Build\usb_midi.c.o C:\froeber\Projects\Kuji_lighter\SmartLighter\Ardu ino\Build\usb_mouse.c.o C:\froeber\Projects\Kuji_lighter\SmartLighter\Ardu ino\Build\usb_rawhid.c.o C:\froeber\Projects\Kuji_lighter\SmartLighter\Ardu ino\Build\usb_seremu.c.o C:\froeber\Projects\Kuji_lighter\SmartLighter\Ardu ino\Build\usb_serial.c.o C:\froeber\Projects\Kuji_lighter\SmartLighter\Ardu ino\Build\yield.c.o C:\froeber\Projects\Kuji_lighter\SmartLighter\Ardu ino\Build\HardwareSerial1.cpp.o C:\froeber\Projects\Kuji_lighter\SmartLighter\Ardu ino\Build\HardwareSerial2.cpp.o C:\froeber\Projects\Kuji_lighter\SmartLighter\Ardu ino\Build\HardwareSerial3.cpp.o C:\froeber\Projects\Kuji_lighter\SmartLighter\Ardu ino\Build\IntervalTimer.cpp.o C:\froeber\Projects\Kuji_lighter\SmartLighter\Ardu ino\Build\IPAddress.cpp.o C:\froeber\Projects\Kuji_lighter\SmartLighter\Ardu ino\Build\main.cpp.o C:\froeber\Projects\Kuji_lighter\SmartLighter\Ardu ino\Build\Print.cpp.o C:\froeber\Projects\Kuji_lighter\SmartLighter\Ardu ino\Build\Stream.cpp.o C:\froeber\Projects\Kuji_lighter\SmartLighter\Ardu ino\Build\Tone.cpp.o C:\froeber\Projects\Kuji_lighter\SmartLighter\Ardu ino\Build\usb_flightsim.cpp.o C:\froeber\Projects\Kuji_lighter\SmartLighter\Ardu ino\Build\usb_inst.cpp.o C:\froeber\Projects\Kuji_lighter\SmartLighter\Ardu ino\Build\WMath.cpp.o C:\froeber\Projects\Kuji_lighter\SmartLighter\Ardu ino\Build\WString.cpp.o -LC:\froeber\Projects\Kuji_lighter\SmartLighter\Ard uino\Build -lm
c:/arduino/arduino-1.0.5/hardware/tools/arm-none-eabi/bin/../lib/gcc/arm-none-eabi/4.7.2/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-signalr.o): In function `_kill_r':
signalr.c:(.text+0x14): undefined reference to `_kill'
c:/arduino/arduino-1.0.5/hardware/tools/arm-none-eabi/bin/../lib/gcc/arm-none-eabi/4.7.2/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-signalr.o): In function `_getpid_r':
signalr.c:(.text+0x28): undefined reference to `_getpid'
c:/arduino/arduino-1.0.5/hardware/tools/arm-none-eabi/bin/../lib/gcc/arm-none-eabi/4.7.2/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-fstatr.o): In function `_fstat_r':
fstatr.c:(.text+0x14): undefined reference to `_fstat'
c:/arduino/arduino-1.0.5/hardware/tools/arm-none-eabi/bin/../lib/gcc/arm-none-eabi/4.7.2/../../../../arm-none-eabi/lib/thumb2\libc.a(lib_a-isattyr.o): In function `_isatty_r':
isattyr.c:(.text+0x12): undefined reference to `_isatty'
collect2.exe: error: ld returned 1 exit status


I figure the problem is that the definition of the functions to handle assert errors hasn't been "fixed" for use on the embedded Teensy platform but is instead trying to call functions that would normally be used on a platform with an operating system. I guess I was wondering if anybody had written their own code to handle assertion errors on this platform? I thought I would ask before trying to "roll my own" solution.

Note, you can build this code for a standard AVR Arduino target (eg an Arduino Micro).

I'm not expecting this to be something that goes high on the list of things that are broken with Teensy and need to be fixed. But I did want to at least add it to the list.

PaulStoffregen
05-28-2013, 07:18 PM
I've never tried using assert() on any microcontroller.

What does it actually do on AVR?

froeber
05-28-2013, 10:41 PM
I've never tried using assert() on any microcontroller.

What does it actually do on AVR?

Hey Paul, Have to get up on the soapbox for a second. Assertions are actually incredibly useful in all sorts of code including all but the smallest, most deeply embedded systems. As I'm sure you know, you can add assertion checks to your code to verify certain design assumptions are met. For instance, in our application we need to have the RTC available and running. So our time initialization code makes sure it is running. And our "read time" function has an assertion that it is running. And at run time, that is verified. That's one example of how we use assertions.

As to what the assertion function does, that is up to the error function that a failed assertion invokes. On non embedded systems, the assertion handler usually displays a message about the check that failed and where the check was located (source file and line) and then does something to trigger entry into a debugger or cause a core dump. In embedded systems the actions are more dependent on the runtime support and what visibility there is into the system. Some embedded systems have debug connections (eg serial ports) that can be used to dump a message. In this case usually it's best to dump a message out the serial port but just use the function name and line number in the message and leave out the file name and expression string since those are usually long strings that would suck up a lot of program memory if there are a lot of assertions. Alternatively, you could store the function/line information in some "no init" area of memory, trigger a restart and pick up and process the failure information following a restart.

At a minimum, and this is what the AVR implementation does, the abort function can be invoked. I'm not sure what the abort function does on different variants of the AVR chips but it might be as simple as disabling interrupts and going into an infinite loop. Ideally, the application uses a watchdog timer to catch such hang ups and causes the system to restart. For debugging, I use an implementation that uses Serial.print to output the information on the failed check and then goes into an infinite loop flashing the LED fast so it's pretty clear something is hosed. Just to save some time hunting around the Arduino distribution, the AVR assert handling can be checked in the hardware/tools/avr/avr/include/assert.h file that's part of the standard Arduino distribution.

There are people who feel you shouldn't use assertions in production code. And if you don't want to that's easily achieved by defining NDEBUG when compiling. The IAR toolset, for instance, automatically defines NDEBUG when you use its RELEASE type build. But others of people, myself included, disagree and, instead, prefer to leave assertions enabled at all times. You get safer code assuming the assertions have been properly used to check that design assumptions/requirements are met and assuming you transition the system to safe operational state (often through a restart) when an assertion check fails.

So, that's my take on it. Assertions are a critical tool for helping develop reliable code and I've seen them adopted and successfully used in many embedded systems. In fact, once exposed to using them and the debug time they save, many engineers I have worked with and mentored don't want to live without assertions. Oh yeah, one last thing is that they serve as a good form of documentation for things like the design assumptions that went into developing the software which makes maintenance easier since they show checks that should never fail if things are working right.

I hope I've answered your question. And I didn't mean to direct this at you. But I figured it was worth the time to add the information to the forum post in case others happen along and read it.

bboyes
04-08-2016, 12:35 AM
Google got me here as I was hoping to use assertions with Teensy as a way to improve exception handling and test code quality. Has anything new happened since this post almost two years ago? I might like the idea of failed assertions going to an abort function which I could modify to log some data to a flash chip and then try to restart safely. I bought Grenning's book "Test-Driven Development for Embedded C", but it was written in 2011 and so far I have not found assert support which works with Teensy 3. If anyone can point me to a solution that would be great... maybe I am missing something simple and obvious... Thanks.

defragster
04-08-2016, 01:08 AM
I've never tried using assert() on any microcontroller.

What does it actually do on AVR?

froeber - I saw this as a statement related to 'any microcontroller' - not the general use or value of asserts.

They don't seem to be defined in the Arduino world - but nothing stops you from doing your own version in code as appropriate to your system. Because not having large memory or processor resources or a fixed display however they are implemented would have to be system specific. Even Serial.print() is not always a usable means of notification - and even then only perhaps during development debugging.

I found this (4 yr old example code) :: How to use assertions with Arduino. (https://gist.github.com/jlesech/3089916)

When it comes to 'microcontroller' usage the option for assert is to test for and handle errors in routine coding at all times in a reasonable way as the device may be unattended - or part of a machine that can't just 'blue screen restart'.

In a car the 'Check Engine' light is from a failed assert - and you have to drive to the repair shop to have the code read to find out you left the gas cap loose and the fuel system detected an anomaly in the vapor recovery system. Or pay a mechanic two hours of labor to diagnose that the light came on because the guy who did the oil change didn't properly clamp the air filter housing on and debris got in an air intake sensor - so the engine management system could not balance the air flow. Another time the car may just fail to start without warning - when it is the same problem that twice before resulted in a 'check engine' light but ran fine for weeks. I have my own code reader now - and these are the problems my cars full of microcontrollers have shown me - and the 'idiot light' for the assert was the system response.

defragster
04-08-2016, 03:03 AM
That linked code of course doesn't work.

There may be better solutions - but using that code, I made this workaround that compiles and functions as indicated. Three lines "//xxx" had to be removed and I edited the function to "assert_3()" to make it unique (and not have Arduino helpfully find unusable code) where it takes 3 parameters. It relies on working Serial.print() and when it is triggered sits in an infinite loop as abort() wasn't supported. Again the user needs to decide - restart the Teensy or stop and sit for debugging purposes.


___ ASSERT FAILED ___ FILE, LINE#, Expression
C:\tCode\TEENSY_INFO\ASSERT\ASSERT.ino
30
false
___ ASSERT FAILED ___ STOPPING !!!!




//xxx #define __ASSERT_USE_STDERR
//xxx #include <assert.h>
void assert_3(const char *__file, int __lineno, const char *__sexp );
#define assert( a ) if (!a) { assert_3(__FILE__, __LINE__, #a); }

// Pin 13 has an LED connected on most Arduino boards.
// give it a name:
int led = 13;

// the setup routine runs once when you press reset:
void setup() {
// initialize the digital pin as an output.
pinMode(led, OUTPUT);
// initialize serial communication at 9600 bits per second.
Serial.begin(9600);
}

// the loop routine runs over and over again forever:
void loop() {
for (uint8_t i = 0; i < 3; i++) {
digitalWrite(led, HIGH); // turn the LED on (HIGH is the voltage level)
delay(1000); // wait for a second
digitalWrite(led, LOW); // turn the LED off by making the voltage LOW
delay(1000); // wait for a second
}

assert(true);
// make assertion failed.
assert(false);
}

// handle diagnostic informations given by assertion and abort program execution:
void assert_3(const char *__file, int __lineno, const char *__sexp) {
// transmit diagnostic informations through serial link.
// Serial.println(__func);
Serial.println(" ___ ASSERT FAILED ___ FILE, LINE#, Expression ");
Serial.println(__file);
Serial.println(__lineno, DEC);
Serial.println(__sexp);
Serial.flush();
// abort program execution.
//xxx abort();
Serial.println(" ___ ASSERT FAILED ___ STOPPING !!!!");
while( 1 );
}

stevech
04-08-2016, 05:27 AM
Google got me here as I was hoping to use assertions with Teensy as a way to improve exception handling and test code quality. I think of assertions as compile time macros to validate type of, number of arguments, etc. Kind of hairy to do with C++. C# or Objective C, well, that's a different galaxy.
Exceptions are run time.
I've not seen C++ exceptions implemented/used in modest microprocessors like Cortex M0-M4.
Python and Javascript have them, due to their bytecode interpreters.