Thank you for this outstanding presentation. So accessing memory-mapped devices in Standard C++ (C++20) is possible after all. This was very instructive.
Thank you very much. I am a hobbyist embedded systems programmer and my girlfriend needed my help building a device to automatically take some sensor data for her research. I'm finding it very difficult to use i2c, spi, uart devices in Linux and interrupt handling outright prohibitively difficult. I'm using c++ because i love it. This helped me a little along the way. I think a microcontroller is more suited for these jobs as most come with a helper library and there is no separate user-space code as in linux.
Very cool talk. I was wondering if C++ would allow for this, and I'm glad it's at least moving in that direction. Now to get the hardware vendors to rewrite their C++ APIs using these techniques -- how amazing would it be if you could talk to the peripherals of an STM32 using this approach? It would be so much simpler than the STM32 HAL, and perform better too! But you could write a *real* HAL to provide abstracted interfaces for things like UART, DMA, etc. Similar to what is going on in the embedded Rust ecosystem.
Great talk as always, but I believe there is still one thing to cover. There is custom new operator implementation and that's fine, but how about missing custom operator delete. In case of UART class object we should not invoke delete operator I guess, since default implementation frees memory on the heap (probably "free" function based implementation) and such invokation would be problematic (or does not make any sense rather) in our case for hardcoded device address. But missing delete invokation may cause some memory leak error detected by some tools (e.g. valgrind), so we got some kind of false positive error then. I can also imagine case where someone uses UART class with smart pointer (with default deleter). So maybe only one missing part of this picture is empty implementation for custom delete operator ?
He seems to"leak" the object and the constructor is not throwing anything so the delete operator should never be called but yes, an operator delete should probably be declared.
10:50 - would be a lot better if the committee FINALLY did what people had been suggesting for decades: give us actual strong type aliases. "using device_regist = uint32_t;" would help a lot more if the compiler then treated it as a separate type so you can not mess up parameter order (still a common problem specifically cause of this). 27:37 - and that is the same problem as before. 30:01 - no, it is not to "inhibit" optimizations... it is to declare that accessing that variable can have other side-effects. It would also have helped if they never made the mistake of "std::atomic".
In my opinion, instantiating for this type of class is often not what we want, which means the constructor and copy operation should be deleted. Consequently, we likely to have a factory method to get the instant, and I think that factory can achieve the same goals as "class-specific new".
great talk! but how do you use interrupts in that manner? think: interrupts have to reset certain bits to prevent multiple fireings of the same interrupt. so interrupt handlers should ideally access the registers as directly as possible. (some interrupts even have to be cleared within x number of cycles...) until no i go with the interrupt service routine being declared as a friend function of the device driver class... this does not feel to great however...
Does anyone have a minimum header file that does this for some trivial memory mapped hardware? It would be super handy to have a place to start from instead of trying to copy all the gems out of this video one at a time.
I would prefer to define the device registers to reside in a dedicated segment and instruct the linker with the starting address of that segment. It's still implementation-dependent, but it calls the constructor and is way better than resorting to reinterpret_cast in my opinion.
9:22 that's more of a subset though "easy to use correctly but hard to use correctly" also implies that it is so easy to use, that you don't get any errors
Using attribute packed is about the worst you can do for device registers. A packed structure will have an alignment of 1 and the compiler has to generate code that will work with that alignment. On architectures without unaligned access, e.g. ARM, that means reading/writing the uint32_t as 4 separate bytes, which is often undefined by the hardware as device registers must be accessed in the right size. So NO NO NO to using attribute packed. Use a plain class and static_assert the offsets.
Isn't it possible to work around the limitation of reinterpret_cast not being acceptable in a contant expression by using bit_cast instead? Also I think the deprecation of volatile is only affecting the "non sensical" use of the qualifier, like declaring a function parameter volatile. It should not affect what was said in this talk.
6:40 Seems like a straw man. No C programmer worth a damn would ever use "int" for a device driver. "int" can vary in size depending on which modern architecture. But even then, the hardware interface would be defined as a packed struct with endianess guarantees.
I agree that I've never seen a plain int in a vendor library. I have absolutely seen assumptions being made about packing, however. You could argue that they aren't good C programmers (I'd happily agree), but that doesn't mean that code like that is in the wild.
Cpp have no standard mechanism to map the register, so instead of mentioning compiler dependent placement, why don't you show how to define class wrapper around already existing c based SDK?
Because C also lack a standard mechanism. The usual C method is to have pointers to each register and he uses the whole talk to speak about better ways than that.
+1 Wow! Very good and powerful features. Never thought about overloading the new operator that way, it really makes sense for embedded programming.
Thank you for this outstanding presentation. So accessing memory-mapped devices in Standard C++ (C++20) is possible after all. This was very instructive.
Thank you very much. I am a hobbyist embedded systems programmer and my girlfriend needed my help building a device to automatically take some sensor data for her research. I'm finding it very difficult to use i2c, spi, uart devices in Linux and interrupt handling outright prohibitively difficult. I'm using c++ because i love it. This helped me a little along the way.
I think a microcontroller is more suited for these jobs as most come with a helper library and there is no separate user-space code as in linux.
Very cool talk. I was wondering if C++ would allow for this, and I'm glad it's at least moving in that direction. Now to get the hardware vendors to rewrite their C++ APIs using these techniques -- how amazing would it be if you could talk to the peripherals of an STM32 using this approach? It would be so much simpler than the STM32 HAL, and perform better too! But you could write a *real* HAL to provide abstracted interfaces for things like UART, DMA, etc. Similar to what is going on in the embedded Rust ecosystem.
I found best talk on memory map for device Driver development and embedded software development..
Amazing presentations..
Thankful for..
This was a great talk. I learned a lot!
Great talk as always, but I believe there is still one thing to cover. There is custom new operator implementation and that's fine, but how about missing custom operator delete. In case of UART class object we should not invoke delete operator I guess, since default implementation frees memory on the heap (probably "free" function based implementation) and such invokation would be problematic (or does not make any sense rather) in our case for hardcoded device address. But missing delete invokation may cause some memory leak error detected by some tools (e.g. valgrind), so we got some kind of false positive error then. I can also imagine case where someone uses UART class with smart pointer (with default deleter). So maybe only one missing part of this picture is empty implementation for custom delete operator ?
He seems to"leak" the object and the constructor is not throwing anything so the delete operator should never be called but yes, an operator delete should probably be declared.
This is a good talk. One thing I'm wondering is does LTO help with performance when using the linker placement method?
10:50 - would be a lot better if the committee FINALLY did what people had been suggesting for decades: give us actual strong type aliases.
"using device_regist = uint32_t;" would help a lot more if the compiler then treated it as a separate type so you can not mess up parameter order (still a common problem specifically cause of this).
27:37 - and that is the same problem as before.
30:01 - no, it is not to "inhibit" optimizations... it is to declare that accessing that variable can have other side-effects.
It would also have helped if they never made the mistake of "std::atomic".
Amazingly informative lecture!
Glad it was helpful!
Nice idea for writing a CPP driver.
What an amazing presentation. I learned so much!!
So glad!
10:00
23:30
25:45
46:24 padding, packing
47:24
Is there any reasons why the Saks brothers dont put the slides on git ?
59:16 - brilliant idea!
59:44 - hack-and-twist.
After the lection you'll know "the right dose of volatility".
In my opinion, instantiating for this type of class is often not what we want, which means the constructor and copy operation should be deleted. Consequently, we likely to have a factory method to get the instant, and I think that factory can achieve the same goals as "class-specific new".
great talk! but how do you use interrupts in that manner?
think: interrupts have to reset certain bits to prevent multiple fireings of the same interrupt. so interrupt handlers should ideally access the registers as directly as possible. (some interrupts even have to be cleared within x number of cycles...) until no i go with the interrupt service routine being declared as a friend function of the device driver class... this does not feel to great however...
I think the normal way is to make the interrupt handler a stativ member.
Hi @urugulu, look up 'Writing better embedded Software Dan Saks Embedded 2018'
Does anyone have a minimum header file that does this for some trivial memory mapped hardware? It would be super handy to have a place to start from instead of trying to copy all the gems out of this video one at a time.
I would prefer to define the device registers to reside in a dedicated segment and instruct the linker with the starting address of that segment. It's still implementation-dependent, but it calls the constructor and is way better than resorting to reinterpret_cast in my opinion.
9:22 that's more of a subset though
"easy to use correctly but hard to use correctly" also implies that it is so easy to use, that you don't get any errors
Using attribute packed is about the worst you can do for device registers. A packed structure will have an alignment of 1 and the compiler has to generate code that will work with that alignment. On architectures without unaligned access, e.g. ARM, that means reading/writing the uint32_t as 4 separate bytes, which is often undefined by the hardware as device registers must be accessed in the right size. So NO NO NO to using attribute packed. Use a plain class and static_assert the offsets.
Isn't it possible to work around the limitation of reinterpret_cast not being acceptable in a contant expression by using bit_cast instead? Also I think the deprecation of volatile is only affecting the "non sensical" use of the qualifier, like declaring a function parameter volatile. It should not affect what was said in this talk.
Unfortunately, the std::bit_cast template is not constexpr when converting to and/or from a pointer type
avr-gcc 5.4.0 in cpp11 mode does not have type_traits...
Good stuff!
Excellent talk.
Amazing lecture! I learned a lot :-)
Glad it was helpful!
6:40 Seems like a straw man. No C programmer worth a damn would ever use "int" for a device driver. "int" can vary in size depending on which modern architecture. But even then, the hardware interface would be defined as a packed struct with endianess guarantees.
I agree that I've never seen a plain int in a vendor library. I have absolutely seen assumptions being made about packing, however. You could argue that they aren't good C programmers (I'd happily agree), but that doesn't mean that code like that is in the wild.
Cpp have no standard mechanism to map the register, so instead of mentioning compiler dependent placement, why don't you show how to define class wrapper around already existing c based SDK?
Because C also lack a standard mechanism. The usual C method is to have pointers to each register and he uses the whole talk to speak about better ways than that.