C++11 library to setup WDT

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

C++11 header-only library to setup a watchdog timer(WDT) using one single line with an expressive and type-safe syntax without any overhead to the generated code.

using namespace avr::wdt;

//turn on the WDT to interrupt at each 4s.
wdt::on(timeout::at_4s, mode::interrupt); 

//turn on the WDT to reset at each 16ms.
wdt::on(timeout::at_16ms, mode::reset); 

//turn on the WDT to interrupt and reset at each 250ms.
wdt::on(timeout::at_250ms, mode::interrupt_reset); 

wdt::off(); //turn off the WDT
wdt::reset(); /reset the WDT

 

https://github.com/ricardocosme/...

github.com/ricardocosme

avrIO: Operation of I/O port pins and I/O registers.

Last Edited: Wed. Mar 31, 2021 - 04:04 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
template<typename Timeout, typename AssumeAtomic = dont_assume_atomic_t>
void on(Timeout, mode = mode::reset, AssumeAtomic = AssumeAtomic{})

OK...  Why is this implemented as a template?

 

 

namespace avr { namespace wdt { namespace detail {

#if __AVR_ATtiny25__ || __AVR_ATtiny45__ || __AVR_ATtiny85__
AVR_WDT_INLINE_GLOBAL_T(avr::io::wdie_t, _wdie)
AVR_WDT_INLINE_GLOBAL_T(avr::io::wdtcr_t, _wdtcr)
AVR_WDT_INLINE_GLOBAL_T(avr::io::mcusr_t, _mcusr)
AVR_WDT_INLINE_GLOBAL_T(avr::io::wdce_t, _wdce)

Ugh.  You should work on a utility that processes an ATDF file into a .hpp file...

 

Every time I look at a C++ implementation of some library/template/whatever (very much including things like the STL), I am confused by a level of complexity that I don't understand the purposes of.
Code is obfuscated and nearly unreadable, compared to what I would write in C or assembly to do "the same thing."  Presumably, I trust, it is because the authors are taking into account factors that I didn't bother with, didn't recognize the need for, didn't find in any of the C++ tutorials I've seen, or just don't understand.  ("why does wdt::on() need to use a separate detail:on()", "why does a WDT library that generates inline code need a 4-deep directory hierarchy", etc.)
 

I guess I could file it all away into a "dude, you're just never going to understand C++ and maybe not even "real" object oriented programming", but I am still curious...

 

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

Quote:

OK...  Why is this implemented as a template?

In this case, I would say that it's only a way to achieve type-safety, but you can use enum class too and without overhead since the optimizer is on(which is by the way a precondition to this work). Thank you to point this out, I will think more about that to experiment a version with enum class.

Quote:

Ugh.  You should work on a utility that processes an ATDF file into a
.hpp file...

Wow, this is very interesting. I have found out: http://packs.download.atmel.com/
Thank you again.

Quote:

Every time I look at a C++ implementation of some library/template/whatever (very much including things like the STL), I am confused by a level of complexity that I don't understand the purposes of.

It's not always easy to talk about this because I have already noticed that people here like to compare languages inside a topic exclusive to one of them and I really ask myself what is the purpose of this... But, Okay, I always trying to do my best to answers questions about C++.

Let's do this again and probably I will start a war of languages and paradigms that I don't have interest to do, but, anyway.

C++ is very interesting when we are using generic programming(GP), compile-time decisions, zero-overhead principle and also metaprogramming when this has a space in the solution. You are mentioning C and assembly, I can't see what you can do in C to write a solution with GP in mind, I also don't see a language support to do metaprogramming not even a support to do compile-time operations. If you don't care about these things, it's okay, as always there is a tradeoff of complexity vs gain and if you're happy with C or assembly, fine.

Quote:

Code is obfuscated and nearly unreadable, compared to what I would write in C
or assembly to do "the same thing."

Yeah, many times the code is complex, and this is a constant when we're exploring the points that I have mentioned above when you're using an old standard like C++98/03. (You can think for example in the usage of concepts without C++20 Concepts)

Quote:

the authors are taking into account factors that I didn't bother with, didn't recognize the need for,

Yes, I think so.

Quote:

"why does wdt::on() need to use a separate detail:on()"

Right. It's common to use the namespace/directory 'detail' when we are developing header-only libraries because it's a way to inform to the user that a set of things doesn't have support through the semantic version scheme and also to hide some nasty details that the author can judge that isn't needed "to the eyes" at first.

Quote:

"why does a WDT library that generates inline code need a 4-deep directory hierarchy", etc.)

There are two popular points about this: one is that some headers are heavy to compile because of the usage of GP and more strongly metaprogramming, but this isn't something that I have already faced in embedded systems, but it's common in non-embedded systems and because of that it's something that runs in my veins. One way to aliviate that is to separate in smaller parts and to include only what is necessary. The second one is that it's a popular way to organize the project to achieve a code more easy to read and to find out something(yeah, it's something questionable and dependes on the taste of the author/community, but it's common and I like it).

Quote:

"dude, you're just never going to understand C++ and maybe not even "real" object oriented programming", but I am still curious...

I hope that my answer help you in some way, but C++ is complex by nature and please, forget about this thing of object oriented programming language... C++ isn't that...

 

github.com/ricardocosme

avrIO: Operation of I/O port pins and I/O registers.

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

>Code is obfuscated and nearly unreadable, compared to what I would write in C or assembly to do "the same thing."

 

You can make C++ just as simple as C.  The trick is to use what you learn to make things simpler, and I think the syntax needed to use your own low level code is a good gauge of where you are at. Another indication is if your source code has you bouncing around from file to file to figure out what is going on vs seeing the code in one place which is short and sweet and to the point. The wdt source code linked in the original post has too many files involved for something that is so simple- why not 1 header file and be done. My 2 cents.

 

Both of the following are header only, and do the same thing. These are for avr0/1, not for old avr as original post is showing. You can keep C++ simple and still get many of its advantages. The C++ version is about as simple as you can get in C++, and there is no need to seek anything 'better' by adding any more complexity. The enum arguments for the functions are a form of compile time assert, so cannot be used wrong. One header file, include it, use it, done. Want to figure out what its doing? one header file, see it, understand it, done. Want to add/modify? one header file, no other files involved.

 

C++

https://godbolt.org/z/fjTadP8Kh

C

https://godbolt.org/z/jEx86Exe3

https://godbolt.org/z/xdWYch4c1

 

The C++ features in use for above- class name so anything inside does not clash with other names, function overloading which works well when it makes sense (window and period for write and read- seems ok, lock for write and read seems odd, so chose isLocked since a bool returned and sounds better), enums as types for function args to get type checking at compile time, private functions which cannot be used from outside, and I guess 'auto' which works well in many cases. Since the class is not separated into declaration/definition, you get implicitly inline code (meaning it can be in a header file where you do not break the one definition rule). All that in a small piece of code, what a bargain.

Last Edited: Tue. Mar 30, 2021 - 11:01 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'll try to keep my questions and comments to the details of C++, rather than starting a language debate...

when we are using generic programming(GP)

I thing I understand the use of templates to achieve generic functions, but part of my problem is that I don't see how the wdt functionality is at all "generic."

 

It's common to use the namespace/directory 'detail' when we are developing header-only libraries because it's a way to inform to the user that a set of things doesn't have support through the semantic version scheme and also to hide some nasty details

I guess.  Ugly stuff like converting the timeout enums to bit patterns for the register.

 

[directory structure] it's common in non-embedded systems and because of that it's something that runs in my veins.

So would you say that a lot of the structure in the WDT "library" is a result of applying common C++ "styles" that aren't really that applicable to the wdt situation itself?  I mean, there is value to that; IFF people are familiar with the style.
 

Why wdt::on() instead of wdt.on() ?  Is that because you created a template instead of an object?  I thought object were the big thing?  Is that a prefered style thing, or a personal decision?

 

using namespace avr::wdt;

wdt::on(timeout::at_4s, mode::interrupt); 

With the "using namespace avr::wdt" in effect, you don't need to specify the "wdt::" part in the call to on(), right?

 

[Curvm:] You can make C++ just as simple as C.

Yes, I realize that.  And that's what I do, pretty much, when I code C++ (for microcontrollers.  Ie, Arduino.)  Maybe a template so I can change a buffer size, and some overloaded functions so that things will work with different argument sets.  But basically "I can write C code in several other languages."  :-)

But the C++ people will sneer at me and say "you're not programming C++; you're just programming C with Classes."  (Which is technically correct, but I'm trying to figure out why it's sneer-worthy.)  ( https://softwareengineering.stac... )

Whenever I look at "more official" C++ code, it doesn't look anything like my code.  It looks more like rcosme's code, and pretty much nothing like what "beginning C++" classes teach, either.  It's almost like there's a separate language for writing the sort of generic libraries that make up so much of C++ actual programs. (Which is perhaps fair.  Maybe it should even be formally separated, although the trend to have all of the language everywhere is pretty old (it always annoyed me that the Pascal Runtime Fucntion allowed things that you couldn't implement in a Pascal user program, like variable numbers of arguments in Print()))
Certainly when I write complex C code, I stray into elements of the language and environment that I don't expect beginners to use...
 

 

The wdt source code linked in the original post has too many files involved for something that is so simple- why not 1 header file and be done. My 2 cents.

Ok.  Your example looks more like code that I'd write (except for all those "auto" return types :-) )
I definitely prefer not having to chase an implementation through a large number of files (especially since I think my relatively archaic tools are pretty likely to think on() is a function that isn't differentiated by the class or namespace it's inside.)

 

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

>I thing I understand the use of templates to achieve generic functions, but part of my problem is that I don't see how the wdt functionality is at all "generic."

 

Generic implies concepts. avrWDT isn't using GP. Templates are only a language resource and it's common to use classes and templates to implement policies. AssumeAtomic is a policy. Sometimes policies have injected behavior or traits but the usage of simple tags is fine too.

 

If you want to see some work to AVR8 using concepts, I invite you to take a look at: https://github.com/ricardocosme/...

 

>So would you say that a lot of the structure in the WDT "library" is a result of applying common C++ "styles" that aren't really that applicable to the wdt situation itself?  I mean, there is value to that; IFF people are familiar with the style.

 

I don't see anything here out of the place. This isn't a toy implementation, it cares about a pleasant experience to understand the API, it cares about semantic versioning, it cares about preconditions that can be established(AssumeAtomic), it is builded with an open door to support other MCUs with other sets of timeouts and I'm not trying to fit the whole thing inside one header.

 

>Why wdt::on() instead of wdt.on() ?

I don't have any state that justifies objects. C++ isn't an OO language, you don't need to fit everything inside a class and instantiate it after that. A better idea is to use OO only when it fits to the problem.

 

>Is that because you created a template instead of an object?

No, I already answer this above. BTW, you can have classes with function templates as member functions.

 

>I thought object were the big thing?

It isn't.

 

>Is that a prefered style thing, or a personal decision?

For sure the personal taste matters, but yes, free function inside good namespaces are something preferred when there isn't state or when you don't need a type with stateless behavior(static member functions).

 

>With the "using namespace avr::wdt" in effect, you don't need to specify the "wdt::" part in the call to on(), right?

It depends, we can have collision if there is another on symbol, this is the case in the demo demo/led.cpp because I'm doing using namespace avr::io but avrIO has an object/tag called on. To me the wdt::on is more expressive than on and there are also the parameters. Without the using we have:

 

wdt::on(wdt::timeout::at_2s, wdt::mode::interrupt, wdt::assume_atomic);

 

It's redundant to me the wdt qualification to define the timeout, timer mode and the policy.

 

>But the C++ people will sneer at me and say "you're not programming C++; you're just programming C with Classes."  (Which is technically correct, but I'm trying to figure out why it's sneer-worthy.)

I'm not judging your C++, but you are judging mine.

 

github.com/ricardocosme

avrIO: Operation of I/O port pins and I/O registers.

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

rcosme wrote:
I don't have any state that justifies objects. C++ isn't an OO language, you don't need to fit everything inside a class and instantiate it after that. A better idea is to use OO only when it fits to the problem.
C++ is an OO language.  Even your WDT example represents a class/object albeit using a singleton design pattern.  If your peers and managers sign off on your ~9 file watchdog implementation, kudos!  Come happy hour, the first round of beer is on you!

C: i = "told you so";

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

>C++ is an OO language.

 

C++ is a multi-paradigm language, an OO language is that one that you always need to fit your stuff inside a class, like Java.

 

>If your peers and managers sign off on your ~9 file watchdog implementation, kudos! 

 

What I think that is funny is the point of interest by the users: the number of files.

github.com/ricardocosme

avrIO: Operation of I/O port pins and I/O registers.

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

rcosme wrote:
C++ is a multi-paradigm language
Sure, OK.
rcosme wrote:
an OO language is that one that you always need to fit your stuff inside a class
More accurate to describe Java as "class oriented" then.  As I've pointed out, your WDT has an "object", the WDT itself.
rcosme wrote:
What I think that is funny is the point of interest by the users: the number of files.
There are many "qualities" to software, weighted in importance differently according to tastes and styles.  If I presented such a scheme in my industry of safety critical avionics, I'd get a lot of criticism and my job would be in danger.

 

Still, I value endeavors such as yours, how do you know what works well without experimenting?  We've had this discussion before, several years ago we had a thread about a highly template-ized example of some AVR project.  There was value there.  Still, it's good to keep things in perspective and be objective in evaluating the pros and cons of a programming design.  The following is an example of code I was given to port from Linux to Windows, which I did, but I had misgivings about the approach in general.

 

bool elib::service::e::Namespace::Entity::list::Handler::handle()
{
    elib::String::Ptr in = boost::dynamic_pointer_cast< elib::String>( input() );
    elib::e::Namespace::Entity::ListServOutput::Ptr out = boost::dynamic_pointer_cast< elib::d::Namespace::Entity::ListServOutput>( output() );
    return handle( *(in.get()), *(out.get()) );
}

 

Imagine dozens of cpp and h files in a maze of source directories, similarly named, filled with this type of thing.  Very clever, but for me, not a good way to do things.

 

I'd suggest, as an exercise, try to get your example into one or two files only.  It's far easier to maintain, and for others to evaluate.

C: i = "told you so";

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

>More accurate to describe Java as "class oriented" then.  

I don't think so.

 

>As I've pointed out, your WDT has an "object", the WDT itself.

The concept of object is something with a variety of interpretations. I use the definition used by A. Stepanov, and using my own words, I would say to you: "object is something that carries value, something that has state." I don't need state here so I don't need an object, it is simpler than that, I only need functions, free functions. I don't know what is your background in C++, but I will try to throw something related to your point about objects that maybe you may appreciate: Elements of Programming - Paul McJones and Alexander Stepanov.

 

>There are many "qualities" to software, weighted in importance differently according to tastes and styles.

For sure.

 

>Still, I value endeavors such as yours, how do you know what works well without experimenting?

Exactly. But here, in the case of avrWDT, there isn't any expressive experimentation related to C++. It's my style, my code and I would say that it's not too far away of others that are presented at well know projects.

 

>Imagine dozens of cpp and h files in a maze of source directories, similarly named, filled with this type of thing.  Very clever, but for me, not a good way to do things.

Yes and I respect that, but this isn't absolute, you're facing someone that do different from you, we're talking about a minor aspect in my humble opinion and about something that isn't easy to write *the* correct way on a stone.

 

>I'd suggest, as an exercise, try to get your example into one or two files only.  It's far easier to maintain, and for others to evaluate.

I have no problem to eventually take another path about this, but until now I really don't think that is the case. As I said before, this work is waiting other MCUs, I have a draft of a branch supporting at43usb320, at76c711, at86rf401, at90pwm81 at94k and atmega128, which means that we're talking, for example, about other sets of timeout values, and I doubt that I would prefer to try to put the whole thing inside one or two headers while we have in the end a user that wants to only see the definitions about his/her MCU.

 

github.com/ricardocosme

avrIO: Operation of I/O port pins and I/O registers.

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

We had been talking about the usage of enum class instead of tags to represents trivial policies.

 

To do an experiment I have replaced the implementation of my interrupt::atomic RAII to use an enum to represent if the abstraction(RAII) should restore or not the state of SREG in the end of the scope. I was optimistic because the compiler knows the policy in compile-time and I have flagged the data member(the dtor should know what is the policy) to hold the policy as const. Below a simplified modified version to illustrate the change:

 

class atomic {
    uint8_t _sreg;
    const at_the_end _mode;
public:
    explicit atomic(at_the_end mode = at_the_end::restore)
        : _mode(mode)
    {
        if(mode == at_the_end::restore) _sreg = io::sreg;
        off();
    }
    
    ~atomic() {
        if(_mode == at_the_end::restore) io::sreg = _sreg;
        else on();
    }
};

 

Running the demos of avrINT I didn't noticed nothing, no overhead, but when I have compiled a project of mine that uses avrWDT I have noticed an increase of 1763 bytes to 1875 bytes in the size of the program. When I have looked at the generated code, I found out that the compiler gaves up to optimize detail::on().

 

Moral of the history: It's a no brainer decision to put informations like that in the type without worries if the compiler will or will not optimize.

 

github.com/ricardocosme

avrIO: Operation of I/O port pins and I/O registers.

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

rcosme wrote:
I don't need state here so I don't need an object, it is simpler than that
But your WDT has state: on/off, expired/not-expired, count, control register.  In terms of design, it's an object, and you've provided a wrapper for it.

 

Singleton pattern - Wikipedia

Wrapper library - Wikipedia

 

rcosme wrote:
I doubt that I would prefer to try to put the whole thing inside one or two headers while we have in the end a user that wants to only see the definitions about his/her MCU.

Sure, but for simplicity, consider a single common header file exposing a watchdog generically, then perhaps a single file implementation for each part or family of parts.  That keeps things nice and tidy. 

 

And, why do you have a subfolder "on" under "detail"?  It contains a single file, and it's not on.hpp, it's common.hpp.  On.hpp is sibling with the folder called on.  Did Paul and Alex provide guidance for this kind of arrangement?  You may think I'm nit picking here, but this is just a watchdog timer.  Someday you may move on to a UART, or PWM, and suddenly complexity might get you.

 

 

C: i = "told you so";

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

In addition to the actual application problem, C ++ has to solve many artificial problems that it created itself. The programming language effort is in no reasonable relation to the simple 8-bit controller hardware. That's really not an advertisement for this programming language. Keep it simple!

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

>But your WDT has state: on/off, expired/not-expired, count, control register.

The hardware has but my abstraction doesn't have one.

 

>In terms of design, it's an object, and you've provided a wrapper for it.

You can model as an object, fine, I dont't think that it's a good idea, but it's up to you.
The wrapper pattern doesn't fit here because I'm not interfacing with another C/C++ abstraction.

 

>Singleton pattern - Wikipedia
There are few good usages to a singleton, it's not always easy to use them because you should be careful about static initialization order fiasco, concurrency, ODR and any problem of a global object, but yeah, there is a space to specific problems. Don't forget that we still talking about state, the only sensible difference is that it's a global one.

 

>Sure, but for simplicity, consider a single common header file exposing a watchdog generically, then perhaps a single file implementation for each part or family of parts.  That keeps things nice and tidy.

I'm "seeing" too many energy on this subject here that I will definitely review the structure, no problem, it's not something too much strong to me if the work have two or ten files with one way or with another one to offer the functions. 

 

>And, why do you have a subfolder "on" under "detail"?  It contains a single file, and it's not on.hpp, it's common.hpp.  On.hpp is sibling with the folder called on.

The original work had one file to each MCU with a WDT that has a different set of timeout values from common.hpp. I have decided to hold the support to other MCUs because I would like to have the chips in hands to do some tests before publish a commit with the support.

 

>Did Paul and Alex provide guidance for this kind of arrangement?
I don't think so or at least no AFAIK. I would say that the authors were more busy putting energy in something more important.

 

github.com/ricardocosme

avrIO: Operation of I/O port pins and I/O registers.

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

>In addition to the actual application problem

I don't see any problem here yet, BTW.

 

>C ++ has to solve many artificial problems that it created itself.

Yeah, the language has many ones. It's a language with +40years but it's updated at each 3 years. This is awesome, we can write powerful and beautiful code with C++ with zero or at least a small overhead in the end mixing paradigms and using updated techniques.

 

>The programming language effort is in no reasonable relation to the simple 8-bit controller hardware. That's really not an advertisement for this programming language. Keep it simple!

I'm satisfied with the usage of the language to AVR8 and I'm not advertising the C++ programming language here, I'm only sharing a solution that I really believe that is interesting to be share. It's not easy to keep simple when you're coding to support different C++ standards, when you're seriously pursuing the best generated code without loosing the code expressiveness that you want in the "application layer", when you're sticking with a semantic versioning scheme, ... It's easy to keep simple with a toy implementation or something narrow to some MCUs.

github.com/ricardocosme

avrIO: Operation of I/O port pins and I/O registers.