Use of ASSERT in AVR C programming?

Go To Last Post
25 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Just read an article by Joseph Beningo (Electronic Design News - EDN, August 20, 2015) on debugging with ASSERT macros. Then I see that AVR-LibC has an ASSERT capability. Yet, I rarely (maybe never) see ASSERT used in code posted here.

 

Do folks not use ASSERT? Or do they scrub them from posted code?

 

Yes, I know that timing will change when ASSERT ia disabled, and that is one of the big negatives that I see. On the other-hand, they seem to be a pretty powerful tool. Any thoughts on using ASSERT?

 

Cheers

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

Last Edited: Wed. Aug 7, 2019 - 10:44 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

assert() only really works if there's some kind of stderr channel for it to output to which often isn't the case with AVR

 

But it us good practice for code to assert()/validate() its inputs. 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 2

ka7ehk wrote:
Any thoughts on using ASSERT?

 

I was once told I needed to assert myself more often.

 

NEver been able to validate if I have or not though as my timings been off.  devil

 

Jim

I would rather attempt something great and fail, than attempt nothing and succeed - Fortune Cookie

 

"The critical shortage here is not stuff, but time." - Johan Ekdahl

 

"If you want a career with a known path - become an undertaker. Dead people don't sue!" - Kartman

"Why is there a "Highway to Hell" and only a "Stairway to Heaven"? A prediction of the expected traffic load?"  - Lee "theusch"

 

Speak sweetly. It makes your words easier to digest when at a later date you have to eat them ;-)  - Source Unknown

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB, RSLogix user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Cliff pointed to the lack of a standard stderr channel. According to the article, ASSERT is only supposed to be used for debugging, not error checking. If this is really the case, then simply executing a break while in debug ought to provide that function, no?

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I assert much, err no that is not it. So what is it when I expect the compiler to do the proper thing (e.g., static) when I don't tell it? It made an auto out of the variable, who would have guessed?  Oh and it ran for over a year like that, and then after a slight unrelated change had problems.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Over in the workd of PIC, Microchip offer this:

Additionally, MPLAB XC C compilers provide a couple of tools that can be helpful.

  • A variant of the standard C assertion mechanism can be used to return to the debugger at certain execution points. The macro __conditional_software_breakpoint(X) is available in assert.h and can be used to halt the debugger.
  •  ...

The macro evaporates away when compiling Production Code.

 

This may come to AVR some day,... Who knows ?

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ka7ehk wrote:
ASSERT is only supposed to be used for debugging, not error checking.
This is the difference between assert() and validate(). assert() is a tool with a "null" implementation in "Release" but a full implementation in "Debug". So, yes, I suppose in that sense it is "used for debugging". validate() is like it  but that remains in the "Release" build (and likely does something "terminal" if activated in the real world code - blue screen of death being one such approach!

 

But the idea is that you do something like:

void set_pixel(uint x, uint y) {
    // screen is 320x240...
    assert(x < 320);
    assert(y < 240);
    // stuff
}    

So when you build this for Debug then start to run the code and someone calls this function with set_pixel(1000, 200); What you want is to be told somewhere

Assertion failed: (x < 320), function set_pixel, graphics.c, line 47

or something. To be told that you need this message to go to an output device.

 

Now you can achieve this:

 

https://www.nongnu.org/avr-libc/user-manual/group__avr__assert.html

 

Note the bit about  __ASSERT_USE_STDERR so if your code is:

#define __ASSERT_USE_STDERR
#include <assert.h>

....

void set_pixel(uint x, uint y) {
    // screen is 320x240...

and elsewhere in the project you have done the FDEV_SETUP_STREAM thing to connect a UART or something to stdout and stderr then, when the code runs and it hits this function with x=1000 then you will see the assert message on the output terminal.

 

But my point was that a load of AVR code does not have an output channel (if it's just reading buttons and lighting lights or controlling servos or pumps or something) then the developer probably won't have bothered with assert() as it has no where to report to.

 

You can see the actual implementation of assert() here:

 

http://svn.savannah.gnu.org/viewvc/avr-libc/trunk/avr-libc/libc/stdlib/assert.c?revision=1944&view=markup

 

and assert.h itself has:

86      #if defined(NDEBUG)
87	#    define assert(e)   ((void)0)
88	#  else /* !NDEBUG */
89	#    if defined(__ASSERT_USE_STDERR)
90	#      define assert(e) ((e) ? (void)0 : \
91	                         __assert(__func__, __FILE__, __LINE__, #e))
92	#    else /* !__ASSERT_USE_STDERR */
93	#      define assert(e) ((e) ? (void)0 : abort())
94	#    endif /* __ASSERT_USE_STDERR */
95	#  endif /* NDEBUG */

 

So if it's not doing the STDERR thing then pretty much all it does in the pump/servo AVR is abort(). abort() itself is very boring indeed:

 

http://svn.savannah.gnu.org/viewvc/avr-libc/trunk/avr-libc/libc/stdlib/abort.c?revision=1944&view=markup

 

So basically a failed assert in a micro without STDERR (using gcc) is a "lock up". It's not going to tell the developer very much. How could he know that the reason for the lock up was that 'x' was greater than 320 ?

 

So, as I say, this is only really of use when there is a reporting mechanism. Which is maybe why its use is not that widespread in AVR?

 

(on "big systems" then assert() is used all the time - or it should be).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ka7ehk wrote:
Just read an article by Joseph Beningo (Electronic Design News - EDN, August 20, 2015) on debugging with ASSERT macros.
Another

Adding Automatic Debugging to Firmware for Embedded Systems (The Ganssle Group)

 

 

"Dare to be naïve." - Buckminster Fuller

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Jim's original article appears to be here:

 

https://www.eenewsembedded.com/archives/pdf-edne-sep-2015.pdf

 

So the entire text about ASSERT (which should be assert() !) is:

Tip #2 – Increase assertion density: The ASSERT macro is a great tool that returns an error message at runtime if the asserted condition is false. Developers can use this macro to verify that assumptions within their code are holding true. Surprisingly, many developers don’t take the time to put assertions into their code. The ASSERT density of a code base can often be the difference between long and painful debug sessions or the trapping of a failed assumption the moment it happens. ASSERT can help a developer discover bugs or assumption failure immediately. What is the ASSERT density of your code base?

I guess it's not so much about the mechanics but why you would use it and what it will prevent. I think my set_pixel() example  with 0..319, 0..239 bounds is probably a good example. The int parameters to the function allow anything up to 32767 (or more) to be passed whereas the active ranges for the variables should never exceed 320/240. Presumably within set_pixel it goes on to do some kind of "data[(y * width) + x] = 1". If the parameters went out of bounds this could easily end up writing outside the defined array size for data[] leading to all kinds of pernicious side effects (who knows what variable my be written if you used set_pixel(512, 512) or something !).

 

So that's what he appears to be driving at - use assert to make sure inputs have expected values and, in case your computational logic is wrong - maybe check "outputs" to be in bounds too.

 

I'm guessing someone may mention Ada at any moment. I don't know the exact syntax but it lets you define variables something like:

int x:0 .. 319;

or something along those line - if you try x = 417 then it throws a wobbly automatically. This is a much stronger policing of bounds than trying to remember to assert() things all over the place. It's inherent in all variables.

 

EDIT: from what I can make out I think the Ada rage limiting may be more like:

CoordX ::= range 0 .. 319
x : CoordX;

which first defines a type with a range then instantiates a variable of that type. But I may have completely misunderstood this - but anyway the idea is there - variables with inherent range checking which makes assert()s look a bit "clunky"

Last Edited: Thu. Aug 8, 2019 - 11:27 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ka7ehk wrote:
If this is really the case, then simply executing a break while in debug ought to provide that function, no?
or the OCDR register.

For PDI and UPDI, can poll for an assertion byte.

A debugger is iffy for environmental chambers and during formal EMI/EMC testing though Microchip does have accessories for some debuggers to increase the target cable's length and reduce di/dt.

 

Atmel-ICE - megaAVR Special Considerations

...

 

IDR/OCDR events

...

When the application program in run mode writes a byte of data to the OCDR register of the AVR device being debugged, the Atmel-ICE reads this value out and displays it in the message window of the software front-end. The OCDR register is polled every 50ms, so writing to it at a higher frequency will NOT yield reliable results. 

...

https://github.com/mraardvark/pyupdi/blob/master/updi/constants.py#L4

The published part of PDI and UPDI share the same commands.

If don't want to go by Python and compensate for the vagaries of an operating system, then the El Tangas method :

UPDI programmer software for Arduino - compatible with avrdude | AVR Freaks

IIRC, it may fit on a mega48; a UPDI monitor would be additional functionality.

UPDI is 900KHz max so UPDI shouldn't be much of an EMI issue whereas PDI will (30MHz max, fast edges anyway)

 


AVR - Calibration & Daq toolchain? | AVR Freaks

 

"Dare to be naïve." - Buckminster Fuller

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Would a linter have detected that issue?

 

"Dare to be naïve." - Buckminster Fuller

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

N.Winterbottom wrote:
This may come to AVR some day,... Who knows ?
MPLAB XC8 v2 for PIC is based on Clang whereas MPLAB XC8 v2 for AVR is based on GCC.

There are advantages to passing the application's source code through two or more different kinds of compilers.

fyi, AVR Clang is nearly imminent so "some day" could at least be evaluated.

The LLVM Compiler Infrastructure Project

...

 

LLVM Release Schedule:

  • 9.0.0:
    • 18 July 2019: branch, then rc1
    • 7 August: rc2
    • 28 August: final

 

...

https://github.com/llvm/llvm-project/tree/release/9.x/llvm/lib/Target/AVR

 

"Dare to be naïve." - Buckminster Fuller

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
... variables with inherent range checking which makes assert()s look a bit "clunky"
similar for Checked C though it's pointers that are bounded.

checkedc-secdev2018-preprint.pdf

(page 2, very bottom of right column)

The Checked C extension extends the C language with two additional checked pointer types: ...

Checked C - Microsoft Research

 

"Dare to be naïve." - Buckminster Fuller

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Jim, I've never used the assert() function, because I've never understood how it works, and looking at the macro did not help either.

Now that I understand it a bit better, I'll try it out in a few test cases and hopefully add it to my quiver of tools!  Thanks.

 

Jim

 

Click Link: Get Free Stock: Retire early! PM for strategy

share.robinhood.com/jamesc3274
stack gold/silver https://www.onegold.com/join/713...

 

 

 

 

Last Edited: Thu. Aug 8, 2019 - 05:43 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Here is the reference to the original article, in case anyone is interested.

 

https://www.edn.com/electronics-...

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

Last Edited: Thu. Aug 8, 2019 - 06:13 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I also need to try the assert function and look into Lint; this has been a timely eye-opener. Thanks.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I sent stdio to the UART and got about 50 characters on stderr, and then it ended. I am not sure why it stopped sending.

 

I like the abort idea, but would rather send messages from flash (e.g., printf_P(PSTR("{\"abort\":\"blue sceen\"}\r\n")); ) and then call abort().

 

Bummer, abort() turns off interrupts (CLI) and then loops, but I want it to wait for the UART to finish, I guess a safe_abort() wrapper is necessary. I probably had 32 characters in the UART buffer when the stderr stream quit blocking, and that allowed abort to run, at which time the UART ceased running ISR's.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

A worst case scenario is stack overflow; try to implement with only registers for storage (registers can be clobbered in this case)

Can be in C (carefully) though assembly language will be easier to maintain.

Some assembly before main, a bit of assembly after main.

 

"Dare to be naïve." - Buckminster Fuller

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I guess my test had 82 characters put on the stack. If so, that may be a reason to avoid the use of assert(). I think the flash base functions (e.g., printf_P) are relatively lightweight on the stack and can provide a lot of details that may fit the upstream connection better.

 

So my thinking at the moment is that an application should set safe pin settings, wait for the UART, and then abort().  

 

I was just looking at the avr-objdump listing of the assert(), and I can not recall a single assembly instruction. VS Code could not IntelliSense any of the parameters passed to assert, so they are all passed in from the compiler (that is probably expected, but it was interesting to ponder).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Do an FDEV_SETUP_STREAM to use a synchronous UART_putchar just for the stderr channel then the assert() printing must finish before it reaches abort(). Forget about using the interrupt buffered UART stuff on that particular channel. (nothing else will likely use stderr anyway)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
assert() only really works if there's some kind of stderr channel for it to output to which often isn't the case with AVR

I define my own assert() to use in my code. It disables interrupts and enters infinite loop that blinks LED. When it happens, I can use debugger to see which assert() failed and it is even better than ordinary assert(), because code is still in state that caused the fault. I use such assert() very often.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Assert lets you make your own runtime error checking so you can protect a method from bogus parameters. I think it would be useful with a debugger running that could stop and show you the statement that asserted so you can say, "Oh, duh. I have value wrong." Well, that's what I use them for in non embedded code. On an avr that runs a servo, you'd have to decide what it should do if the assert fails. Probably nothing, hoping the assert caught any problems during development when you had the debugger attached.

The largest known prime number: 282589933-1

It's easy to stop breaking the 10th commandment! Break the 8th instead. 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

To be fair, I think the most common use of "assert" in non-embedded code is to check for valid pointers to dynamically allocated memory.

Dynamic allocation is less common in deeply embedded systems, so...  fewer asserts.

 

Not having an error channel is occasionally deeply disturbing.  There is a bunch of Arduino Core code (not the best example of modern programming practices, I know) where it will check for "NOT_A_PIN" in digitalWrite or similar.  But it's source is questionable, and it doesn't DO anything, leaving the sketch free to go on and be ... random.  It's SO tempting to us one of the GPIO registers (on AVR) and just come up with several "classes of error" that the program could check for, if it wanted...

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

westfw wrote:
Dynamic allocation is less common in deeply embedded systems, so...  fewer asserts.
What's common for such systems is ESD/EFT/lightning with system dependent GCR/SEU/SEL.

Flash memory doesn't have as long a life as SRAM; some harsh environment MCU are zero flash (all SRAM, power on : bootload : go)

A constant that's invalid can be reloaded from flash if the ECC is complete, precise, and correct (exception else assertion)

westfw wrote:
Not having an error channel is occasionally deeply disturbing.
The opposite for systems with telemetry.

A harsh instance of that was AF447 where the first data was from the low speed up-link.

The value(s) of assertions can ideally be sent as that rate is very low.

A wee bit of assembly code to repeatedly send one byte (assertion) on one pin until the watchdog bites.

On the bench, captured by a logic analyzer; in practice, telemetry.

 


ECC - Error Correction Code

EFT - Electrically Fast Transient

ESD - Electrostatic Discharge

GCR - Galactic Cosmic Ray

SEU - Single Event Upset

SEL - Single Event Latchup

Automated messages - Air France Flight 447 - Wikipedia

 

"Dare to be naïve." - Buckminster Fuller

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What's common for such systems is ESD/EFT/lightning with system dependent GCR/SEU/SEL.

But now you're talking about something other than the "compiles to null in production code" nature of "assert."

 

(part of the problem with assert is that you're frequently uncertain about whether it is "debug only" or "permanent."  Yeah, there are equivalent explicit versions, but once you lose the simplicity...  And there's the whole separate issue of "which things do I check all the time, and which checks can I skip in production code?")