Skip to main content
Explorer
August 2, 2024
Question

Calling a virtual function seems to stop the execution of the MCU (Nucleo F446RE)

  • August 2, 2024
  • 1 reply
  • 922 views

I'm trying to port over a JUCE framework project (a wavetable VCO), and I'd like to test it on a Nucleo-F446RE that I got about a year ago.

My entire build system is a clone of this lady Emilie Gillet's code base that she used to build eurorack modules.  It's not quite HAL, and I guess not quite Bare Metal, but somewhere in between?

My repository is here.

https://github.com/eclewlow/eurorack/tree/master/waves2

I'm just porting over a little bit of code at a time.  One thing I ran into was virtual functions being called just causing the flow of execution to stop.  I don't think it's a hard fault, but the execution of the MCU definitely stops.

For example, in bootloader.cc

https://github.com/eclewlow/eurorack/blob/master/waves2/bootloader/bootloader.cc

void Init() {
 System sys;

 sys.Init(false);

 sys.StartTimers();

 GPIO_ResetBits(GPIOA, GPIO_Pin_5);

 BufferAllocator allocator(shared_buffer, 16384);

 childA.Paint();
 Engine* e = &childA;

 volatile size_t counter = 1000000;
 while (counter--);

 e->Paint(); // the execution reaches here and then stops, and the LED does not blink

 GPIO_SetBits(GPIOA, GPIO_Pin_5);

}

int main(void) {
 Init();

 // after Init calls, infinitely blink once per second
 // If I call the virtual function e->Paint() in Init(), then
 // this following code will never execute.
 while (1) {
 if((system_clock.milliseconds() / 1000) % 2 < 1) {
 GPIO_SetBits(GPIOA, GPIO_Pin_5);
 } else {
 GPIO_ResetBits(GPIOA, GPIO_Pin_5);
 }
 }
}

I'm a newb to compilers and linkers, so if there is an error there, I would have no idea.

I've been messing around with compiler and linker flags, and the linker script.

One thing I noticed was at first the vtable entries in the .map file were missing?  And it wasn't until I edited the linker script file did the vtables show up.  However, it didn't fix the issue.

https://github.com/eclewlow/stmlib/commit/c1e4fb1913e813cdc76abfb34ccc4ab5793ce8a3

https://github.com/eclewlow/stmlib/blob/master/linker_scripts/stm32f4xx_flash.ld

The .map file seems to show a vtable entry for each virtual function, and they are assigned to different spaces in memory.  Are the memory addresses somehow incorrect maybe?

*(.rodata)
 *(.rodata*)
 .rodata._ZTVN6waves26EngineE
 0x0000000008000600 0x10 build/waves2_bootloader/bootloader.o
 0x0000000008000600 vtable for waves2::Engine
 .rodata._ZTVN6waves26ChildAE
 0x0000000008000610 0x10 build/waves2_bootloader/child_a.o
 0x0000000008000610 vtable for waves2::ChildA
 .rodata._ZTVN6waves26ChildBE
 0x0000000008000620 0x10 build/waves2_bootloader/child_b.o
 0x0000000008000620 vtable for waves2::ChildB

I'm building the code on a MacOS Sonoma 14.5, with the arm-4.8.3 release, as recommended by Emilie.  Although, what's really recommended is building in a virtual machine, which I'm not doing.

    This topic has been closed for replies.

    1 reply

    Super User
    August 2, 2024

    When you debug the code and step through execution on that line, what happens?

    ECLEW.1Author
    Explorer
    August 3, 2024

    Thank you for your response.

    So I don't know how to debug without CubeIDE, so I created a new CubeIDE project targeting my current Nucleo board.

    There was no need to debug there.  The code runs fine.  The virtual function is called followed by blinking the LED.

    I tried to dig around a little bit: checking out the makefile and linker scripts that are auto-generated by CubeIDE.  They are different than the build system I was using.

    I'll just use CubeIDE.