LPC4330, C++ and SysTick

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

LPC4330, C++ and SysTick

2,285 Views
C-Coder
Contributor III

I'm using a custom board that is based off of the LPC4330-Xplorer board.

 

I took the usbd_rom_libusb, lpc_board_ngx_xplorer_4330, and lpc_chip_43xx projects out of the lpcopen_3_02_lpcxpresso_xplorer4330.zip file. Using those projects I tweaked the example code just a little (swapping the cr_startup_lpc18xx.c for cr_startup_lpc43xx.c, and changed the USB read/write usage to match a PC side test application). All other aspects of the example were left as provided by NXP, and it worked relatively well.

 

Now I want to start using C++ for some code, so I have done the following:

 

1 - renamed cr_startup_lpc43xx.c to cr_startup_lpc43xx.cpp

2 - renamed libusbdev_main.c to libusbdev_main.cpp

3 - modified .project file to include the ccnature

4 - modified project settings via the UI to have the same includes, libraries, etc now that it is using the C++ compiler and linker as it did when it was only using the C compiler and linker. BTW, it is linking with Newlib (nohost).

 

Making JUST those changes, the application will appear to hang on startup. The debugger will not make it to main. If I comment out this line:

 

__asm volatile ("cpsie i");

 

in ResetISR that is in cr_startup_lpc43xx.cpp, I will get to main, but as soon as I re-enable interrupts, the application will appear hung again. Pausing execution in the debugger, I find that execution is looping in the default SysTick_Handler infinite loop.

 

I know that I can add my own SysTick handler that doesn't have a loop and execution is be fine. But what is the C++ compiler/linker doing to drastically change that behavior? Are there any other steps I've missed?

 

Thanks,

 

Torin

Labels (2)
0 Kudos
16 Replies

2,173 Views
C-Coder
Contributor III

Everyone is missing the original point of the post. I DID NOT CHANGE ANY CODE. I merely changed from .c to .cpp, added the CCNATURE to the project and built. I didn't add constructors or destructors. I didn't add C++ specific headers or libraries, or even specific defines (other than those enabled by the C++ compiler). If you look at the cr_cpp_config.cpp code that was suggested, you'll see that it only adds definitions for new, new[], delete, delete[] __aeabi_atexit, and __verbose_terminate_handler. None of which were being called. It was all straight C code exactly from the example from NXP, merely compiled with the C++ compiler. No interrupts were specifically enabled. No handlers were added or removed.

 

After switching to a newer version of MCUXpresso and creating test C and C++ projects (as opposed to trying to use one of NXP's existing demo/example projects), both are exhibiting the same behavior. This is fine by me. Previously, only the C++ behaved that way, so we wanted to know why it was behaving differently. With both behaving the same now, it doesn't matter. We can now move on with doing the work we truly wanted to do.

 

We should all be wondering though how the NXP examples ever worked without a custom SysTick handler. As the code is written right now, there's no way a novice user could ever build and run an example without issue.

 

Thank you,

 

Torin

0 Kudos

2,166 Views
converse
Senior Contributor V

You may not think you are using new/delete etc, but they are used by the C++ initialisation code. This is why they are important. If you look in the startup code, you will see:

#if defined (__cplusplus)
    //
    // Call C++ library initialisation
    //
    __libc_init_array();
#endif

__libc_init_array() is the C++ startup code and it is this which will make use of those functions defined in cr_cpp_config.cpp (I have no idea why these weren't added to the startup code - they could have been!).

 

0 Kudos

2,157 Views
converse
Senior Contributor V

BTW: If you want to use Systick, look at the SysTick example - periph_systick

0 Kudos

2,154 Views
C-Coder
Contributor III

None of this is about SysTick. I did not want/need SysTick. I merely pointed out that we were getting into the default handler which never exits. I never enabled it. I've searched all of the code and not found where it is being enabled. I've searched the band new projects and not found where it is enabled. My guess is that the LPC4330 (and probably others) have it enabled by default.

 

Either way, we can move on from this issue as we now know what to expect and have always known how to handle it.

 

Thanks

 

Torin

0 Kudos

2,139 Views
frank_m
Senior Contributor III

> My guess is that the LPC4330 (and probably others) have it enabled by default.

No, definitely not. I had been working with a LPC43xx some while ago.

> I never enabled it. I've searched all of the code and not found where it is being enabled. I've searched the band new projects and not found where it is enabled.

Perhaps from a SDK module that is included in your project by default. SysTick_Config() is a CMSIS function from ARM, for functionality present in all Cortex M devices. And I would not exclude the possibility that this module is ... problematic.

I would check the map file, if the respective symbol (SysTick_Config) is listed. The map files gives the source module of each symbol as well.

It could be done without CMSIS functions, by directly accessing the peripheral registers. But I have never seen any Cortex M SDK / BSP code doing that.

0 Kudos

2,133 Views
C-Coder
Contributor III

I'll agree to disagree. Grepping the entire workspace folder reveals only core_xx.h references to SysTick_Config. Stepping through the code from ResetISR onward, I do not find any references to it, nor to the registers specifically. Like I said, anyone can download the examples and see what I'm seeing. If someone can find it where it is being enabled, I would be ecstatic to be proven wrong as a great learning experience for me.

 

But, I have also stated that now that I am seeing the same behavior with C and C++ with new projects using newer MCUXpresso builds, I'm happy and can move on.

 

Thanks,

 

Torin

0 Kudos

2,130 Views
frank_m
Senior Contributor III

I don't have any LPC4330 hardware here, so I would have a hard time to test and verify anything.

Perhaps a data watchpoint at the Systick config register could help to identify the source of the access.

BTW, CMSIS provided some other, "hidden" means for startup initialisation. The usual CMSIS-compatible startup code contains a call to a SystemInit() function, which is usually implemented in a module named system_<mcu-name>.c. This function is called after the RAM section initialisation, before main. And many toolchains keep the CMSIS includes and modules in separate folder in ther installation path, not with the projects.

I had battled occasionally with toolchains when porting my older code. Those projects already contained all the (older) required CMSIS sources, but the toolchains by default insisted on a newer version it was shipped with.

0 Kudos

2,171 Views
frank_m
Senior Contributor III

> We should all be wondering though how the NXP examples ever worked without a custom SysTick handler. As the code is written right now, there's no way a novice user could ever build and run an example without issue.

I don't know the LPC43xx SDK and the examples, and you might be true.

But the problem is not the default handler nor a missing proper handler, but the call to SysTick_Config(). This enables the SysTick interrupt in the first place.

I would check where this init call happens in your code, or the SDK example.

I worked with a few LPC546xx SDK examples, and they either leave SysTick alone (except for the WEAK default handler in the startup), or provide both the init call and a proper handler.

Some middleware might implicitly use SysTick as well. Some RTOSs and a competitor's HAL framework come to my mind.

0 Kudos

2,208 Views
frank_m
Senior Contributor III

> 1 - renamed cr_startup_lpc43xx.c to cr_startup_lpc43xx.cpp

In no IDE/environment I know this was a valid way to go. I did not try that with MCUXpresso IDE, but believe it is not different.

C and C++ projects use to have different startup code, one significant difference are constructor calls.

I would suggest to create a new project, as C++ from the getgo.

2,281 Views
converse
Senior Contributor V

Have you used extern c on methods called from c, such as the interrupt handlers?

 

https://embeddedartistry.com/blog/2017/05/01/mixing-c-and-c-extern-c/

 

 

0 Kudos

2,278 Views
C-Coder
Contributor III

Converse - In this particular case, I'm not asking how to get it to call my own SysTick handler, but yes I would use extern "C" if I had it declared/defined in C++ code. I'm just concerned about why merely changing from C to C++ the behavior changes. With the C code, execution never entered the SysTick handler, but with C++ it enters as soon as interrupts are enabled.

 

Thanks,

 

Torin

0 Kudos

2,274 Views
converse
Senior Contributor V

Please post your project so we can see what you have broken!

0 Kudos

2,263 Views
C-Coder
Contributor III

You can download the example here:

 

https://www.nxp.com/design/microcontrollers-developer-resources/lpcopen-libraries-and-examples/lpcop...


Find "NGX Xplorer LPC4330 board" and click the v3.02 link for LPCXpresso v8.2.0.  Open a workspace in MCUXpresso and import the 3 projects that I listed. Rename the two files that I mentioned, add the ccnature to the .project file. Now you have my project. You can completely ignore any USB changes I made because they're not related and not necessary. The problem is strictly related to renaming the two files (and I suspect that just one of them renamed is enough) to cause the SysTick interrupt to be enabled and coded as soon as interrupts are enabled.

0 Kudos

2,242 Views
converse
Senior Contributor V

When you get the IDE to creat a C++ project, it adds an additional file to the project - cr_cpp_config.cpp. It doesn't look like you have added this file. I'm pretty certain that this is the cause of your problems.

 

I suggest you create yourself a C++ project, using the MCUXpresso wizard and then copy this missing file across to your project.

 

I know that NXP recommend that to create a C++ project from a C project, you should create a project and copy your files across rather than editing the nature...

0 Kudos

2,198 Views
C-Coder
Contributor III

I took it a step farther and tried to debug a brand new C++ project that I let MCUXpresso create. Sadly it does the exact same thing (as soon as interrupts are enabled, the SysTick ISR is fired which goes into the default code, which is an infinite loop). In the process of doing this test I switched to a different machine that has a slightly newer version of MCUXpresso (11.2.1). I decided to use it to create a brand new C project (in addition to the brand new C++ project). It also behaves exactly the same. With new projects one for each language, they both behave the same way. I'm OK with this. So we can chalk it up to a non-issue now.

 

Torin

0 Kudos

2,190 Views
frank_m
Senior Contributor III

> Sadly it does the exact same thing (as soon as interrupts are enabled, the SysTick ISR is fired which goes into the default code, which is an infinite loop).

Which isprobably unrelated to your C/C++ change.

The startup use to contain a default SysTick handler which behaves that way (endless loop), but is defined as WEAK. This means, you are supposed to provide your own implementation. Otherwise, do not enable the interrupt.

 

0 Kudos