Author Topic: non static C++ class objects with interrupt routines  (Read 4586 times)

0 Members and 1 Guest are viewing this topic.

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 18852
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: non static C++ class objects with interrupt routines
« Reply #50 on: June 09, 2026, 11:43:05 am »
"Optimization problems" mostly stem from a divergence between programmer assumptions and official C/C++ standard semantics. To avoid them, developers must know and understand exactly what the standard guarantees—and what it treats as undefined behavior. And yes, the detailed semantics of the abstract C or C++ machine are not as trivial as one may think. There are many potential pitfalls.

Indeed and sometimes the optimiser doesn't even throw errors.  It just warns and removes code which would lead to "undefined behaviour".  Depends on architecture etc. etc.

An example is the default optimiser on some architectures removing "null pointer" access as an "optimisation".  I was trying to write a "bootloader on 68k" it took quite a while to work out the optimiser had just deleted the funcitonal code writing to address 0x0.

In C++ "type transparency" and "type honesty", especially in the interface between C++ and C and direct memory manipulation are required for many optimisations and if the optimiser cannot "trust" your types it will through other warnings.  Things like casting an object or struct to a (char*) through a lower level and casting it back again somewhere else.  The optimiser will get annoyed.   Frame alignment warnings etc.

Well I get no errors, a much smaller output and it never runs so it is something fundamental.

what is the best way to cast n C++? I am doing it C style (type)(object)
 

Offline Nominal Animal

  • Super Contributor
  • ***
  • Posts: 8343
  • Country: fi
    • My home page and email address
Re: non static C++ class objects with interrupt routines
« Reply #51 on: June 09, 2026, 04:55:34 pm »
My point is that on embedded targets, you can use C runtime and freestanding C environment when you use GCC or Clang to compile freestanding C++, and limit yourself to the features that do not need runtime support.  See the ISO C standard or published drafts for the definition of freestanding environment.

what is the best way to cast n C++?
 

Offline cv007

  • Super Contributor
  • ***
  • Posts: 1056
Re: non static C++ class objects with interrupt routines
« Reply #52 on: June 09, 2026, 05:00:16 pm »
I have said this many times, but I start in -Os, stay in -Os, and finish in -Os. Let the compiler optimize fully so you can catch little problems as you create them instead of accumulating them to be debugged when you decide to change optimization later. It takes only a missing volatile keyword to create a problem, and it will not show up in -O0 but it surely will when you increase optimization.

I don't use the debugger, so my debugging consists of reading the generated asm and using the uart. I don't find using -Os a problem when debugging, and since I stick to a single optimization I get a good feel for what the compiler is doing and what it will do at that level, and can more easily find my way around the generated asm and notice when something looks out of place. Any problem I create is normally realized soon after the code was created, so I have a good idea where to look. If there is a problem in understanding what is happening in some code due to optimization, I'll (very rarely) make the function noinline long enough to figure out what its doing and if its correct. If I create myself an exception before I can get the uart working, I can still blink out the pc address that is on the stack which will get me close to the problem.


Quote
what is the best way to cast n C++? I am doing it C style (type)(object)
You first avoid it if possible, and its possible more than you think. When not possible, use the C++ style casting for this reason- you can more easily search for it in your code.

Find all the casting done in this example, hint- search for '_cast'
https://godbolt.org/z/PP64Yxvc1

 

Online paulca

  • Super Contributor
  • ***
  • Posts: 6216
  • Country: gb
Re: non static C++ class objects with interrupt routines
« Reply #53 on: June 10, 2026, 08:35:55 am »
what is the best way to cast n C++? I am doing it C style (type)(object)

This is where it gets academic though.  When you are using objects from 'classes' the types themselves hold their "cast-ability".

The largest and most common is the "is-a" relationship.  If the "is-a" test holds, you don't need to cast it.  "is-a" tends to work 'peer and parent' direction.

"If a ByteStream is-a Stream", then you can treat a reference to "ByteStream" as "Stream", but NOT vice versa.

So, the most likely place to put a cast is in a 'specialised path'.  If 90% of the code works on "Stream", but 10% needs to know it is infact a byte stream, say to implement (WordAlign()) ..  You will see blocks which compare the "Stream reference" by it's underlying type, if it "is a" it will get cast.

pseudo:
IF streamRef is-a ByteStream:
   ByteStream bStreamRef = (cast ByteStream)streamRef
   bStreamRef->WordAlign()

However.  This is considered an "anti-pattern".  If you add another type of stream you need to update the cast selectors.  These "selectors" will not stay in one place.  They will spread... everywhere.  If every bit of code that needs to know it's a XStream and not a YStream starts doing the above, it makes it impossible to change the Stream hierarchy later.  If you look at something like the history of the Java language and runtime libs, you will find this mistake repeated and entire hierarchies end up marked as "Deprecated".... left to rot.

That is where the design patterns come in.  The above has many different solutions, all of them have their own costs to.  Right up to things like the "Visitor pattern" which is honestly a beast, especially when people implement it with "inline anonymous members".

But...  in my opinion.  If you have a small known number of types, a fairly obvious and fairly static hierarchy.  A simple "is-a" selector is fine.

A pattern that I personally prefer is the "As a" pattern.  If you have a class which is of TypeX, but could be of sub TypeY, rather than burden the caller with determining the relationship, you move that into the class itself.  The syntax depends on a few things, even within C++ though.  If you have "overloaded return types" you can just do:

TypeY tY = typeXInstance.getAs();

If you don't have overloaded return types, you have to pass an example:

TypeY tY = typeXInstance.getAs(TypeY.class);

annoying in older versions of Java and still some APIs today you have to do:

TypeY tY = typeXInstance.getAs(new TypeY()); // and burn memory churn unless the JIC optimised it.

The point is and its sort of "half" the visitor pattern without too much overhead, just the caveat that it will couple your hierarchy internally which itself can bite you later if the complexity scales.

Couple = "Change this" and you need to "Change that"

In MCU land, your types and structures will be fairly limited in size and complexity anyway.  It is unlikely, assuming the hypotethical Stream hierarchy, you will probably only deal with "CharStream", "ByteStream", maybe, "BufferedStream".  The number of locations it will matter are probably fixed.

The visitor pattern and that level of abstract become more "fitting" if you are looking at the "Object hierarchy" UI widget in something like Autodesk Fusion.  Each individual object in the drawing is there, there are dozens, maybe hundreds of types of "thing" in that tree, different icon, different label, different right click context.  That would make one very large "is a" selector.  And when you add a new "View" or "Gadget" as a feature, a nightmare to update.  There going to the length of a visitor pattern makes sense.  The visitor pattern is sort of like a "double callback" so the caller does not need to know the type of the callee... in advance.  The leaf types provide a method to get their Visitor instance.  The caller then calls the generic method on the Visitor instance.  That then calls the actual leaf node contextually.  So the "contract" is met without both ends having to know what each other was.  A bit like "Double NAT"'ing a OneToMany VPN link.

C++ specifically has a "streams" component built into the runtime, so you would be better starting there before implementing a "Streams" tree.  Although, as an academic OOP vehicle to learning, it is fairly close to the MCU layer.  While being OOP.

Streams is all that:

cout << "Hello" << (2,3) << "World"

stuff.  Ill be honest and say most of my C++ in the real world stayed well away from these.  All our string routines where C-like direct memory manipulation, including inline ASM for "tokenizing".
"What could possibly go wrong?"
Current Open Projects:  68000 Self Build computer + OS.
 

Online paulca

  • Super Contributor
  • ***
  • Posts: 6216
  • Country: gb
Re: non static C++ class objects with interrupt routines
« Reply #54 on: June 10, 2026, 08:50:48 am »
Well I get no errors, a much smaller output and it never runs so it is something fundamental.

What platform are you running this on and what debugger?

On Linux I am fairly sure you can launch an ELF in HALT and step into it's init code without a break point.

That may or may not hold try in MCU land though.

If you using GCC you should be able to "keep ASM output" files and look for obvious things.  Or get Claude or LLM to help compare.

"strace" on the process might give hints.

The problem if on MCU is...  where is the runtime actually executing and how do you break point it?  I think that's where Nominal Animal is probably correctly leading you.

EDIT:  BTW.  I have been "white rooming" an OS.  Introducing C looked beign at first.  Until I wanted to create "User Processes".  Then it gets complicated fast.  The lesson however was that "C" is not the bottom layer.  A whole ton of things C does are hidden in CRT hooks in the OS... or the MCU runtime.  I suppose the good news is that MCUs tend to only have "one runtime" and you don't need to communicate between runtimes.... and address spaces.
« Last Edit: June 10, 2026, 08:58:21 am by paulca »
"What could possibly go wrong?"
Current Open Projects:  68000 Self Build computer + OS.
 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 18852
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: non static C++ class objects with interrupt routines
« Reply #55 on: June 10, 2026, 10:08:53 am »
This is a pic32cx1025sg41128, I an debugging in Ozone with a J-Link

Yes when I get time I intend to continue with copilot or whatever it is in VSC in looking at the problem. It has already helped me discover __COMPILER_BOUNDRY(); for while loops that check/wait for register syncronizations. But this was not the fix and it put an include to the cmsis header all over the place which says in it, not to include it directly, it is included via pic32c.h I suspect.
 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 18852
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: non static C++ class objects with interrupt routines
« Reply #56 on: June 10, 2026, 10:09:40 am »
In terms of casting it is more a case of basic variables uintX_t types.
 

Offline m k

  • Super Contributor
  • ***
  • Posts: 3440
  • Country: fi
Re: non static C++ class objects with interrupt routines
« Reply #57 on: June 10, 2026, 06:54:16 pm »
I would avoid casting int types to dynamic direction.
Advance-Aneng-Appa-AVO-Beckman-Danbridge-Data Precision-Data Tech-Fluke-General Radio-H. W. Sullivan-Heathkit-HP-Kaise-Kyoritsu-Leeds & Northrup-Mastech-OR-X-REO-Schneider-Simpson-Sinclair-Tektronix-Tokyo Rikosha-Topward-Triplett-Tritron-YFE
(plus work shop of the world unknowns)
 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 18852
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: non static C++ class objects with interrupt routines
« Reply #58 on: June 11, 2026, 06:48:05 am »
I would avoid casting int types to dynamic direction.

What? I'm talking about uint8_t to uint32_t for example. So when I run a display buffer I have to work in terms of single bytes to fill it but when it comes to sending it over SPI I can load 4 bytes at a time into the TX buffer reducing the interrupts and processing by 75%.
 

Online paulca

  • Super Contributor
  • ***
  • Posts: 6216
  • Country: gb
Re: non static C++ class objects with interrupt routines
« Reply #59 on: June 11, 2026, 08:04:21 am »
I would avoid casting int types to dynamic direction.

What? I'm talking about uint8_t to uint32_t for example. So when I run a display buffer I have to work in terms of single bytes to fill it but when it comes to sending it over SPI I can load 4 bytes at a time into the TX buffer reducing the interrupts and processing by 75%.

I read this and immediately started towards an "OOP" version of it.  You see it looks like a buffer with multiple "views".  If you think about it, it's the same data, expressed in different ways.  So my first thought was a Buffer with two iterators/accessors and a self alignment method.

However.  That full OOP pattern "encapsulates" the raw array completely.  It "owns" the memory, gate keeps it, and has opinions.  This I expect, correct me if I am wrong, if going to cause you issues when you want to use GFX functions to 'draw' into the DisplayBuffer.

I assumed that you are using a C Gfx API.  Protoytpes like:

drawPixel( uint8_t* buf, int x, int y, int color );

The "long option" is to write a C++ shim, binder over the gfx library.  Internalise the "buf" to the instance of the class and make the "drawXYZ" methods methods.

However.  The simpliest OOP "finess" towards learning C++ would be a basic "adapter" class.

Claude came up with tihs:
Code: [Select]
class DisplayBuffer {
public:
    // Write interface — thinks in pixels
    using pixel_iterator = uint8_t*;
    pixel_iterator begin_pixels();
    pixel_iterator end_pixels();

    // Transmit interface — thinks in packed words
    using word_iterator = uint32_t*;
    word_iterator begin_words();
    word_iterator end_words();

    // Or expose directly to a HAL send function
    const uint32_t* data() const;
    size_t          word_count() const;

private:
    // Single backing store, aligned for both access patterns
    alignas(uint32_t) uint8_t _buf[WIDTH * HEIGHT];
};

For the encapsulated buffer and this 'adapter' for raw access to the pointer

Code: [Select]
class WordView {
public:
    explicit WordView(const DisplayBuffer& buf)
        : _words(reinterpret_cast<const uint32_t*>(buf.pixel_data()))
        , _count(buf.pixel_count() / 4)
    {}

    const uint32_t* data()  const { return _words; }
    size_t          size()  const { return _count; }

    // Range-for support
    const uint32_t* begin() const { return _words; }
    const uint32_t* end()   const { return _words + _count; }

private:
    const uint32_t* _words;
    size_t          _count;
};

It does seem to be a long way to go to "focus" the pointer casting into one place.

I think it's OTT on it's own merit, but if you were to "port" the gfx methods into the class you could allow the underlying buffer to be basically hidden entirely.

It added this interesting little point:
Code: [Select]
The Subtlety Worth Teaching
The uint8_t* → uint32_t* reinterpretation is only defined behaviour if:

The storage is properly aligned (alignas(uint32_t) handles this)
The size is a multiple of 4 (a static assert on WIDTH * HEIGHT catches this at compile time)
You go through std::launder or a memcpy-based approach if you're being strict about aliasing rules

That last point is where many MCU developers get burned — it works on their compiler with -O0 but breaks under optimisation. The class is the right place to contain that careful reasoning once, rather than leaving it to every caller.
« Last Edit: June 11, 2026, 08:07:12 am by paulca »
"What could possibly go wrong?"
Current Open Projects:  68000 Self Build computer + OS.
 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 18852
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: non static C++ class objects with interrupt routines
« Reply #60 on: June 11, 2026, 10:35:26 am »
The buffer sits in a (monochrome) display class that handles putting character pixels into the buffer from a font array.

It is actually in my SPI class that I was casting the uint8_t * to uint32_t* so that I could step through 4 bytes at a time to load into the buffer. What I am doing now which may be a bit of a hack and have you screaming at me is instead of casting the 8 bit pointer to 32 bit on every interrupt I have a union of an 8 and 32 bit pointer that is stored in the class and is the pointer passed to it when a transmission is started.

So if the size of the buffer to transmit is not a mutiple of 4 the 8 bit pointer is used, if the buffer to transmit is a multiple of 4 the 32 bit pointer is used with the top count divided by 4. At this point recasting is not necessary as a pointer to an 8 bit address is the same value as a pointer to the same 32 bit address. This obviously only work on these pointers :)

C++ casting discussions seem to be about classes or other C++ objects rather than raw value variables.
 

Offline m k

  • Super Contributor
  • ***
  • Posts: 3440
  • Country: fi
Re: non static C++ class objects with interrupt routines
« Reply #61 on: June 11, 2026, 02:42:16 pm »
I would avoid casting int types to dynamic direction.

What?

You used capital X, so I thought it's exact.
Had to check it and learned that it has a compiler selectable fastest width for the hardware.
Advance-Aneng-Appa-AVO-Beckman-Danbridge-Data Precision-Data Tech-Fluke-General Radio-H. W. Sullivan-Heathkit-HP-Kaise-Kyoritsu-Leeds & Northrup-Mastech-OR-X-REO-Schneider-Simpson-Sinclair-Tektronix-Tokyo Rikosha-Topward-Triplett-Tritron-YFE
(plus work shop of the world unknowns)
 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 18852
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: non static C++ class objects with interrupt routines
« Reply #62 on: June 11, 2026, 03:14:43 pm »
I would avoid casting int types to dynamic direction.

What?

You used capital X, so I thought it's exact.
Had to check it and learned that it has a compiler selectable fastest width for the hardware.

I just put the capital X as a filler for 8, 16, 32 or 64.

alignas(uint32_t) sounds like a good idea. What I have noticed is that things tent to get alligned as uint64_t
 

Offline SiliconWizard

  • Super Contributor
  • ***
  • Posts: 17705
  • Country: fr
Re: non static C++ class objects with interrupt routines
« Reply #63 on: June 11, 2026, 03:42:39 pm »
I would avoid casting int types to dynamic direction.

What? I'm talking about uint8_t to uint32_t for example. So when I run a display buffer I have to work in terms of single bytes to fill it but when it comes to sending it over SPI I can load 4 bytes at a time into the TX buffer reducing the interrupts and processing by 75%.

So I suppose you want to cast a pointer to the buffer, like you would in C. In this case, I think reinterpret_cast<> is what should be used, like:

uint32_t * buff32 = reinterpret_cast<uint32_t *>(buff);

Make sure of course that the buffer is properly aligned, or that your target supports unaligned access if not.

Note that using the C-style cast will also work: (uint32_t *) buff. I think idiomatic C++ prefers using an explicit cast operator giving you control over the cast, rather than the C-style which can be implemented in various ways depending on the source and destination types. But for typical cast between scalar types, C-style cast is equivalent to static_cast<> and equivalent to reinterpret_cast<> for pointers to scalar types. It's when you start casting between pointers to classes/struct that the cast can become non-trivial and requires something explicit to make sure it does what you intended it to do. Just my 2 cents.
 

Offline cv007

  • Super Contributor
  • ***
  • Posts: 1056
Re: non static C++ class objects with interrupt routines
« Reply #64 on: June 11, 2026, 06:13:25 pm »
Non-functioning but possibly logically correct, spi simple example similar to what you may be doing-
https://godbolt.org/z/rjsWvad8o

I don't see anything wrong with using unions. There are certainly more ways to handle this, but if a union eliminates the requirement to cast in other parts of the code then I think it is a good thing. Thinking of alternatives to eliminate the need to cast is worth a little effort, but obviously not always possible.
 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 18852
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: non static C++ class objects with interrupt routines
« Reply #65 on: June 11, 2026, 06:31:33 pm »
The union is perfect, it eliminates the need to store the same pointer twice and the casting operation or to do a casting operation on every interrupt call because however you want to treat the pointer in both cases it points to the same address.
 

Offline gf

  • Super Contributor
  • ***
  • Posts: 1817
  • Country: de
Re: non static C++ class objects with interrupt routines
« Reply #66 on: June 11, 2026, 07:44:27 pm »
Type punning via union is safe in C, but in standard C++, it is undefined behavior.

https://en.cppreference.com/cpp/language/union:
Quote
It is undefined behavior to read from the member of the union that wasn't most recently written.

Safe alternatives are memcpy() and std::bit_cast (C++20).

EDIT:

We can generalize this principle to any memory object. As a rule of thumb, reading a memory location using a type different from the one most recently written triggers undefined behavior (UB) under Strict Aliasing rules. The only exceptions are the types char, unsigned char, and std::byte.

Examples:

Quote
float *pf = ...;
int *pi = reinterpret_cast<int*>(pf);
*pf = 5.3f;
int i = *pi; // UB!

Quote
float *pf = ...;
char *pc = reinterpret_cast<char*>(pf);
*pf = 5.3f;
char c = *pc; // Safe - exception for type char

Quote
float *pf = ...;
*pf = 5.3f;
int i = std::bit_cast<int>(*pf); // Safe - bit_cast

Quote
float *pf = ...;
*pf = 5.3f;
int i;
memcpy(&i, pf, sizeof(int)); // Safe - memcpy()

EDIT:

So, what's wrong with the first example above? The Strict Aliasing rule of the C++ standard legally allows the compiler, for instance, to re-order the code to read the int value before the float is written, ending up effectively with:

Quote
float *pf = ...;
int *pi = reinterpret_cast<int*>(pf);
int i = *pi;
*pf = 5.3f;

Since pf and pi point to different types, the compiler is not obliged to preserve the chronological order of the two assignments.

[ In practice, compilers/optimizers only assume that different types don't alias when they cannot trace the actual data flow. If they can trace it at compile time, they usually optimize based on the known addresses. However, the pitfall is that you can never guarantee the compiler will successfully trace it. ]
« Last Edit: June 12, 2026, 07:14:32 am by gf »
 

Offline Alien Brother

  • Regular Contributor
  • *
  • Posts: 63
  • Country: fr
Re: non static C++ class objects with interrupt routines
« Reply #67 on: June 12, 2026, 12:20:27 am »
So when I run a display buffer I have to work in terms of single bytes to fill it but when it comes to sending it over SPI I can load 4 bytes at a time into the TX buffer reducing the interrupts and processing by 75%.
An alternative is to still use uint8_t or unsigned char in the SPI code and manually construct the values that will go into the peripheral registers (like, buf[ i ] | (buf[ i+1 ] << 8 ) | ...). On an STM32, I end up also writing into the SPI FIFO byte by byte to not bother with their DSIZE setting.
« Last Edit: June 13, 2026, 02:53:26 pm by Alien Brother »
 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 18852
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: non static C++ class objects with interrupt routines
« Reply #68 on: June 12, 2026, 08:11:34 am »
Code: [Select]
if (word_mode == true)
        instance->SPIM.SERCOM_DATA = bufferPtr.u32[tx_counter]; // ((uint32_t *)buffer_ptr)[tx_counter];
    else
        instance->SPIM.SERCOM_DATA = bufferPtr.u8[tx_counter]; // buffer_ptr[tx_counter];

    instance->SPIM.SERCOM_INTFLAG = SERCOM_SPIM_INTFLAG_DRE_Msk; /* Clear DRE interrupt flag */

    tx_counter++;

    if (tx_counter == buffer_size)
    {
        instance->SPIM.SERCOM_INTENCLR = SERCOM_SPIM_INTENCLR_DRE_Msk; /* Clear DRE and TXC interrupt enable bits */
        instance->SPIM.SERCOM_INTENSET = SERCOM_SPIM_INTENSET_TXC_Msk;
        tx_counter = 0;
    }

I don't know how that compares to loading one byte at a time into the 4 byte buffer for transmission, but if you are OK sending 1 byte at a time then I guess that sort of efficiency is not something you are too worried about.

I don't know if optimization speeds up memset() but a simple loop on words (after recasting of course) is way faster with -O0.

I'm still getting compiler messages of the like:
C:/Program Files (x86)/Arm/GNU Toolchain mingw-w64-i686-arm-none-eabi/bin/../lib/gcc/arm-none-eabi/15.2.1/../../../../arm-none-eabi/bin/ld.exe: C:/Program Files (x86)/Arm/GNU Toolchain mingw-w64-i686-arm-none-eabi/bin/../lib/gcc/arm-none-eabi/15.2.1/../../../../arm-none-eabi/lib/thumb/v7e-m+fp/hard\libc_nano.a(libc_a-readr.o): in function `_read_r':
readr.c:(.text._read_r+0x10): warning: _read is not implemented and will always fail

even though I have LDFLAGS += --specs=nosys.specs --specs=nano.specs # use newlib-nano and don't link in syscalls stubs
in the make file.
« Last Edit: June 12, 2026, 08:23:02 am by Simon »
 

Offline SiliconWizard

  • Super Contributor
  • ***
  • Posts: 17705
  • Country: fr
Re: non static C++ class objects with interrupt routines
« Reply #69 on: June 12, 2026, 02:23:19 pm »
I don't know if optimization speeds up memset() but a simple loop on words (after recasting of course) is way faster with -O0.

At least on Cortex-M targets using newlib, I can confirm memset() has relatively poor performancen just like memcpy(). The compiler can inline some memset() (or memcpy) calls with optimizations enabled, but in practice the inlining cases are limited to very short lengths and it falls backs to calling the actual library function otherwise, and it's not particularly well optimized. Copying or setting using a manual loop in 32-bit chunks is much faster in all cases I've ever tested on those targets (again as long as the buffers are 32-bit aligned). Anyone let me know if you can see otherwise with real tests.
 
The following users thanked this post: Alien Brother

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 18852
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: non static C++ class objects with interrupt routines
« Reply #70 on: June 12, 2026, 02:34:36 pm »
OK, just making sure I was not being a smart arse only to find that with optimization it suddenly works properly.

Don't take my word for it but I am pretty sure that even for something like 24 bytes a loop on word chucks was faster by something like 60 cycles. I know it's only 0.5µs on my 120MHz chip but still.
 

Offline SiliconWizard

  • Super Contributor
  • ***
  • Posts: 17705
  • Country: fr
Re: non static C++ class objects with interrupt routines
« Reply #71 on: June 12, 2026, 03:34:03 pm »
Anyone curious can have a look at the source code to understand why that is so:

https://github.com/eblot/newlib/blob/master/newlib/libc/string/memset.c

It does attempt to be smart about grouping by 32-bit words if it can and set the rest with byte accesses, but:
- The code handling that is generic (as it must handle all cases gracefully), so there are a lot of extra tests on address alignement and steps. So there is overhead unless the buffer size is fairly large.
- As you can see with "#if !defined(PREFER_SIZE_OVER_SPEED) && !defined(__OPTIMIZE_SIZE__)", it only uses the "optimized" code if those macros are not defined, and while this would need to be checked for the toolchain you use, I suspect that either __OPTIMIZE_SIZE__ or PREFER_SIZE_OVER_SPEED or both are defined when building the *nano* flavor of  newlib (to optimize code size), so that it directly falls backs to just byte access.

The same approach is used for memcpy().

So, as ClapGPT would say, no, you're not imagining it.
 
The following users thanked this post: Alien Brother

Offline cv007

  • Super Contributor
  • ***
  • Posts: 1056
Re: non static C++ class objects with interrupt routines
« Reply #72 on: June 12, 2026, 06:34:57 pm »
Quote
even though I have LDFLAGS += --specs=nosys.specs --specs=nano.specs # use newlib-nano and don't link in syscalls stubs
That combo may have been needed previously to get rid of the warnings but I think you can skip the nosys.specs now depending on the gcc version you have. I think somewhere between gcc 11 and 14 that changed.

On gcc 14.2 I use only nano.specs for stm32. I also use -nostartfiles as there appears to be nothing useful provided by the linked in crt object files. That does require you to provide an empty extern "C" void _init(){} function because the library function __libc_init_array() has a call to it inside (the _init/_fini otherwise being provided by crti.o were also doing nothing useful).
 

Offline Manicken

  • Newbie
  • Posts: 5
  • Country: se
Re: non static C++ class objects with interrupt routines
« Reply #73 on: June 13, 2026, 01:38:26 pm »
When I first read your post, I couldn’t understand what you were trying to do, so I put it into ChatGPT.
It generated code that I could actually understand.

Just as you said in the first post, I think this is what you want: it enforces constructor initialization,
and avoid to use init later, which in big projects can be missed, or if you share the code.

Code: [Select]

class Encoder {

public:

// preferred to use if toolchain allows it
using EncoderCallback = void (*)(int32_t delta);
// always working on all toolchains uncomment it you can use it
//typedef void (*EncoderCallback)(int32_t delta);

Encoder() = delete; // delete default constructor to enforce callback assignment

Encoder(EncoderCallback callback) : callback(callback) {
// do other init here
}

// actually it's preferable to separate the definition from the declaration so the definition need to be in a cpp file
// both included here for simple example code
void onInterrupt()
    {
// just a high level abstraction
// better is to read pins depending on what the MCU can handle
        bool a = readA();
        bool b = readB();

        // hardware decode step (fast ISR path)
        int32_t delta = (a == b) ? +1 : -1;
        count += delta;
    }

// actually it's preferable to separate the definition from the declaration so the definition need to be in a cpp file
// both included here for simple example code
    void task()
    {
__disable_irq();
int32_t snapshot = count;
__enable_irq();
        if (snapshot != lastCount)
        {
            int32_t delta = snapshot - lastCount;

            lastCount = snapshot;

            if (callback)
                callback(delta);
        }
    }

private:
volatile int32_t count = 0;
    int32_t lastCount = 0;

    EncoderCallback callback;

};



// global accessible ptrs
static Encoder* encoderInstance1;
static Encoder* encoderInstance2;

void EXTI0_IRQHandler()
{
    encoderInstance1->onInterrupt();
}

void EXTI1_IRQHandler()
{
    encoderInstance2->onInterrupt();
}



void Encoder1_Event(int32_t delta) {

}

void Encoder2_Event(int32_t delta) {

}


int main()
{
// constructors of Encoders runs here.
    static Encoder encoder1(Encoder1_Event);
static Encoder encoder2(Encoder2_Event);

// store globally so interrupts can access them
    encoderInstance1 = &encoder1;
encoderInstance2 = &encoder2;

// enable interrupts code here


// main loop
while(1) {
encoder1.task();
encoder2.task();
// ... other tasks here
}
}


a special note here
that particular PIC do have one HW quadrature decode embedded
which can be used if you only have one rotary encoder
but if you have many then the software ones are more flexible
 

Offline Alien Brother

  • Regular Contributor
  • *
  • Posts: 63
  • Country: fr
Re: non static C++ class objects with interrupt routines
« Reply #74 on: June 13, 2026, 03:21:02 pm »
!defined(__OPTIMIZE_SIZE__)
Looking it up, GCC defines it when compiling with -Os. Quite a sneaky way to configure a library.
« Last Edit: June 13, 2026, 03:33:40 pm by Alien Brother »
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf