This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

C++ simple question

I am using uVision 4.21 MDK_ARM to learn how to program C++ on MCB1700.

I have create a new project and add 2 files (startup_CPL17xx.s / system_LPCxx.c) to startup group. Then I add main.cpp to Sources Files group. The project options are:

Device: LPC 1768
Target: Default
Output: Default
Listing: Default
User: Default
C++: Default
Asm: Default
Linker: Default
Debug: Use ULINK Cortex Debugger
Utilities: Default

Then I change the startup_LPC17xx.s
Stack Size: 0x0000 1000
Heap Size: 0x0000 1000

My main.cpp code:

#include <vector>
#include <string>

int main (void)
{
  int i = 0;
  int sum = 0;
  std::vector<int> vInt;
  std::string str = "Hello, C++";

  vInt.push_back(1);
  vInt.push_back(2);
  vInt.push_back(3);
  vInt.push_back(4);
  vInt.push_back(5);

  sum = vInt.size();
  sum = 0;
  for(i=0; i<5; i++)
  {
    sum += vInt[i];
  }
  while(1);
  //return 0;
}


I build the project successfully and download it to MCB1700 board "OK". Then I hit "F9" to set break point at line "vInt.push_back(1);". After I select Debug->Start Debug Session, the break point is not reached even I hit "F5" many times. Why?

By the way, I find there are 2 windows: Disassembly Window and Code Window (for main.cpp). How could I step through the code on C++ source code level and how could I step through the code on Assembly level?

Parents Reply Children
  • with the PC register and the yellow arrow pointing to the BKPT instruction (0x00001482)

  • Aoso, For C++ case, after debug session started, I can not use "F5" to set break point and menu Debug->Inser/Remove Breakpoint is greyed out.

  • BKPT 0xAB is the "semi-hosting" interface.

    You need to either use semi-hosting (sending I/O to your PC via the debugger) or retarget the stdio of your library (sending stdio through a UART or other interface). I added a dummy "retarget.c" to a sample project with your main.cpp and could get it to work.

    Here are two links that explains the general idea:

    Semi-hosting
    infocenter.arm.com/.../index.jsp

    and retargeting
    infocenter.arm.com/.../index.jsp

    In essence, what's going on is this: the C++ library eventually calls down to C functions to handle stdio. On a PC the output would go to the standard device defined by the OS but on an embedded system you have to bring your own. I usually send stdio to the trace port via the ULINK Pro or a serial port.

    You can start with the retarget.c from here:

    infocenter.arm.com/.../index.jsp

    You'll need to supply a UART_write/UART_read or just define them as empty functions for starters.

  • Thank you for your reply. I will look at it.

    By the way, for main.c case, why I can not watch the variables value? But I do find if I change the code to:

    int i = 0;
    int sum = 0;
    
    int main (void)
    {
    //      int i = 0;
    //      int sum = 0;
    
            for(i=0; i<10; i++)
            {
                    sum = sum + i;
            }
            i = sum;
            while(1);
            //return 0;
    }
    


    Move i / sum outside of the main, then I can watch the value change. Weird.

  • You'll have to look at the assembly code the compiler produces. The debugger may not be able to tell how the variable is stored so it can't display it. It's probably just kept in a register for the duration of the loop.

    Making variables global forces the compiler to store them in actual memory locations and then you can inspect them.

    Usually switching optimisation off solves many of these types of problems but some optimisations are so basic the compiler will never avoid them.

    Frequently programmers will use 'volatile' to force variables into memory but in the case of a local variable on the stack that's rarely a good idea.

  • Why can't it inspect variables in registers, then?

    "some optimisations are so basic the compiler will never avoid them"

    So debug tools should really be able to cope with them!

  • Thank you, Andrew.

    After I twisted for awhile, my main.cpp can be run.

    But again, I can not check the local variable value. This is not convenient for debugging the code.

    Also, even worse, after I move string variable definition outside the main function. I still can not watch the string variable (its value always 0).

    My C++ code:

    #include <iostream>
    #include <vector>
    #include <string>
    
    int sum = 0;
    std::string str;
    
    int main (void)
    {
      int i = 0;
      std::vector<int> vInt;
    
      str = "Hello, C++";
    
      vInt.push_back(1);
      vInt.push_back(2);
      vInt.push_back(3);
      vInt.push_back(4);
      vInt.push_back(5);
    
      sum = vInt.size();
    
      sum = 0;
      for(i=0; i<5; i++)
      {
        sum += vInt[i];
      }
      while(1);
      //return 0;
    }
    

  • I think now you're really running into limitations debugging C++ with uVision. The 0 value is obviously incorrect but even if you were able to put str in a watch window you'd just see the whole std::string class. A C++ "capable" debugger would give you the option of showing the C-style string that would be returned by c_str().

  • Thank you, Andrew Queisser.

    Another question for my little project described in this thread.

    I have arbitrary set Stack size to 0x0000 1000 and Heap size to 0x0000 1000. But I am not clear what size I should set to use memory more efficiently.

    I program z-80 / 8051 about 30 years ago on assembly level (not C level). I am not clear which part the compiler will put into the stack (I know function call parameters, registers, status, etc might be keep in the stack and memory allocation like "new" keyword in C++ will put variable in the heap. What else? I would like to estimate the heap / stack usage so that I could assign the size wisely without waste memory.

  • Hi David,

    Regarding your stack/heap question - you already sum it up pretty well. Broadly speaking, there are three ways your program will use RAM: global and static variables in the data segments, automatic (local) variables on the stack and dynamically allocated memory on the heap.

    It's really hard to come up with recommendations for the stack size since it depends almost entirely on the depth of your call tree. If you don't have recursively called functions you can monitor your stack usage by pre-initializing it with a constant value and checking how much gets consumed. It's hard to analyze stack usage from your code alone since the optimizer will make many variables disappear.

    The heap usage is under your control, for the most part. Whenever you use malloc or new the heap is used so you know how much gets consumed. Libraries will also use the heap but you can usually figure out who uses how much.

    All this get's much more complex with C++, especially if you use RTTI and exceptions. You'd be well advised to study what other embedded experts have to say about using C++ in embedded systems. Embedded.com has a lot of information on that topic, e.g. here:
    www.eetimes.com/.../Embedding-C-or-C--Is-that-really-the-question-

    Andrew