This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

TM4C123AE6PM: Hard fault using PWM

Part Number: TM4C123AE6PM
Other Parts Discussed in Thread: TM4C123GH6PM

Tool/software:

I initialize a PWM with a pulse width of 0; everything runs fine.  Some time later I change the pulse width, the system continues for a while, then issues a hard fault; at this point the CFSR is 0x00020000 which is "Instruction makes illegal use of EPSR.  The HFAULT register is 0x40000000 which is "Forced Fault, see other registers to determine the source", but as can be seen in the CFSR the BFSR and MMFSR are both 0.  Code as follows:

    SysCtlClockSet(SYSCTL_SYSDIV_2_5 | SYSCTL_XTAL_20MHZ | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN);  // 80MHz

    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);

    SysCtlPeripheralEnable(SYSCTL_PERIPH_PWM1);

(delay a little bit here)

    GPIOPinTypePWM(  GPIO_PORTE_BASE, PD_Pwm1_2_BIT );

    GPIOPinConfigure(GPIO_PE4_M1PWM2);

    SysCtlPWMClockSet(SYSCTL_PWMDIV_64);             // 1250 KHz PWM Clock.

    PWMGenConfigure(PWM1_BASE, PWM_GEN_2, PWM_GEN_MODE_DOWN | PWM_GEN_MODE_NO_SYNC );

    PWMDeadBandDisable(PWM1_BASE, PWM_GEN_2);

    PWMGenPeriodSet(PWM1_BASE, PWM_GEN_2, 6250);     // 200Hz

    PWMPulseWidthSet(PWM1_BASE, PWM_GEN_2, 0);        // 0% duty cycle

    PWMOutputUpdateMode(PWM1_BASE, PWM_OUT_4_BIT | PWM_OUTPUT_MODE_NO_SYNC);

    PWMGenEnable(PWM1_BASE, PWM_GEN_2);

some time later I issue:

    PWMPulseWidthSet(PWM1_BASE, PWM_GEN_2, 69);       // ~10%

some significant time later the hard fault occurres.

Thanks, Doug

  • Hi Doug,

    some time later I issue:

        PWMPulseWidthSet(PWM1_BASE, PWM_GEN_2, 69);       // ~10%

    some significant time later the hard fault occurres.

      What other peripherals ar running other than PWM1_GEN_2? 

      How long does it take for the hard fault to happen? Here you said after some significant amount of time. Can you quantify? 

      I kind of doubt by changing the PWM duty cycle will cause the device to hard fault. It is most likely due to something else.

      What is your current stack size? Can you try to increase the stack size? Does it make a difference?

  • Other peripherals: UART1, UART4, EEProm, PWM0, PWM1, CAN1, all GPIO.  Don't know how long, I could raise a pulse on a GPIO when I set the pulse width and turn it off on the fault and I'll let you know.  My dispatcher sits in a loop and sleeps the cpu until an interrupt - in this case just the system timer which fires every 10ms.  After I set the pulse width I've traced the dispatcher running for a while.  Current stack size 1024 bytes.  Will run some tests and get back to you.

    I'm surprised the TI or someone else hasn't written a better fault handler for FaultISR that would go query the registers and the stack and put some meaningful diagnostics messages in some memory locations before it goes into the while(1) loop that can be queried in the debugger.  It's a pain when I'm trying to get work done to spend half a day or more researching and understanding how to debug this issue, in my case so far not knowing enough to understand what's happening.

    Thanks, Doug

  • Hi Doug,

      To unwound to the offending line of code that caused the fault, you can follow this app note. Diagnosing Software Faults in Stellaris® Microcontrollers

      In this below post, Chester offers a solution to unwound the offending line using GEL script which I find quite useful.  

    https://e2e.ti.com/support/tools/code-composer-studio-group/ccs/f/code-composer-studio-forum/810598/ccs-tm4c1294kcpdt-how-do-i-get-the-stack-unwound-in-exception-handlers/3026599#3026599

  • Hi Charles,

    Have been reviewing the example gel file and have a couple of questions.  Even though the TM4C is a Cortex M4, it appears that is uses the CortexM3.util.gel, correct?  I can't find how to use my modified CortexM3.util.gel, documentation states that it is loaded via the Tiva TM4C123AE6PM.ccxml file but it has no reference to a .gel file.

    Thanks, Doug

  • Hi Doug,

      M3 and M4 have the basically the same architecture-wise. The major difference is the addition of DSP instructions support on M4 and also the option to support FPU. The CortexM3.util.gel is just a file name carried over from the predecessor. You can rename it to anything like CortexM4.util.gel if you want. 

    The target configuration file specifies tm4c123gh6pm.gel and the tm4c123gh6pm.gel specifies the CortexM3_util.gel. 

  • Ah, I didn't catch that.  Thanks

  • OK, the changes to the .gel files worked great in tracking this down (why isn't this a standard part of CCS ?), the error happens in a strcmp() call where one of the addresses is illegal.  It gets called because a function is returning to the wrong address - why this is happening is a big head scratcher.

    This function receives a message that a command had been issued and scans through the monitor command acronyms to find the command to run.

    When I run the command that causes the fault, instead of returning to after the last command on this function (that called the command), it returns to the green highlighted line and the strcmp() causes the fault.  This is the function that was called:

    If I comment out the Validate1or2uintParams() call the fault does not happen.  Here is that function:

    Which has been used is several other projects too.

    Here's the anomaly,  following is the Stack Usage graph, I upped the stack from 1024 to 2048 and these graphs still look like they did at 1024.

    ???  Ideas?  Thanks, Doug

  • Things really seem to be screwed up.  When I start a debug session now, instead of halting at main(), this is what shows up:

  • Just for ducks, I created a new workspace and added in the files and manually set the parameters to what they were in the other workspace.  Anomalous behavior remains.

  • Hi Doug,

    Here's the anomaly,  following is the Stack Usage graph, I upped the stack from 1024 to 2048 and these graphs still look like they did at 1024.

    Did you also change the stack pointer accordingly in the linker command file? See below example.

    SECTIONS
    {
    .intvecs: > APP_BASE

    .text : > FLASH
    .const : > FLASH
    .cinit : > FLASH
    .pinit : > FLASH
    .init_array : > FLASH

    .vtable : > RAM_BASE
    .data : > SRAM
    .bss : > SRAM
    .sysmem : > SRAM
    .stack : > SRAM


    }

    __STACK_TOP = __stack + 2048;

  • The .map file shows the stack is 2048 regardless of what is set in the linker command file.

  • Hi Doug,

      You must have __STACK_TOP = __stack + 2048 to define where is the start of the stack. You could declare  ex. 8kB of stack but if your stack pointer still points to 512B instead of 8kB then it does not solve any stack issue. The SP is the first thing the processor reads after reset. Unless you have already adjusted the SP and still get hard fault, this will be the thing to change in the linker command file. 

  • Hi Doug,

      You must have __STACK_TOP = __stack + 2048 to define where is the start of the stack. You could declare  ex. 8kB of stack but if your stack pointer still points to 512B instead of 8kB then it does not solve any stack issue. The SP is the first thing the processor reads after reset. Unless you have already adjusted the SP and still get hard fault, this will be the thing to change in the linker command file. 

      I will also suggest you try with a larger size, something like 4kB and if it works, you can reduce to suitable size for what is needed. 
  • Setting both to 4096, behavior the same.

  • That is kind of odd. I'm not sure why the fault since you said it took a while after the PWM interrupt. If you create a small program that uses Validate1or2uintParams, will it fail?

  • I'm in the process of writing my own sscanf() function using strtok and stoi in the SetPWM program itself and will let you know the results tomorrow.

  • Hi Doug,

      When you get a hard fault, is Validate1or2uintParams called the very first time? Or Validate1or2uintParams has been called many times without a problem until the last time when it is called and faulted? 

      I will suggest you put a breakpoint in Validate1or2uintParams and single step the disassembly window to find out which instruction is the offending instruction causing the fault. 

      I also wanted to give you a heads-up that I will be out of office the entire next week. Please expect delay in my response. 

  • Hi Charles,

    It is the first time it is called.  I'll try another command that will use it and see what happens (good idea on your part).  I've got several things I am going to try.  It acts like something is altering the return PC on the stack.  I'll see if I can figure out a way to view that call stack.

    I'll miss you !

    %-)  Doug

  • Very interesting.  if I parse the command line using strtok() and atoi() everything works fine.  If I instead use sscanf() (not calling Validate1or2UintParams()) it crashes in the same way, the SetPWM() function returns to the same wrong address.  Calling a different function that uses Validate1or2UintParams() works fine.

    I found it !!  Obviously the stack was getting trashed somehow.  One of the parameters to Validate1or2UintParams was an enumeration which I had cast to a uint* for the call to work, assuming from what I had read about C, C++ that an enumeration could be cast to an int, evidently the compiler used a different size for the enumeration, maybe an 8 or 16 bit value.  Since the two parameters were local to SetPWM(), they were on the stack and the return address got trashed because of the cast.  Learn something new every day.

    Hopefully you're going on vacation next week and if so, have a great time.

    Thanks, Doug

  • Hi Doug,

    I found it !!  Obviously the stack was getting trashed somehow.  One of the parameters to Validate1or2UintParams was an enumeration which I had cast to a uint* for the call to work, assuming from what I had read about C, C++ that an enumeration could be cast to an int, evidently the compiler used a different size for the enumeration, maybe an 8 or 16 bit value.  Since the two parameters were local to SetPWM(), they were on the stack and the return address got trashed because of the cast.  Learn something new every day.

    Glad that you found the issue. I'm also learning something new as I have not used Validate1or2UintParams before. 

    Hopefully you're going on vacation next week and if so, have a great time.

    Thank you! Much appreciated.