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

printf data abort - (again)

Hello,

I've problems with the printf() routine. I receive frames over the ethernet in a very short intervall. If I received a frame I set a flag in the ISR.

void ethernet_interrupt(void) __irq
{
        unsigned int status;
        AT91PS_EMAC pEmac = AT91C_BASE_EMAC;

        status = pEmac->EMAC_ISR;

        if (status & AT91C_EMAC_RCOM)                               /* Receive complete */
                Emac_Receive++;

        // clear receive status register
        pEmac->EMAC_RSR  &= ~(AT91C_EMAC_OVR | AT91C_EMAC_REC | AT91C_EMAC_BNA);
        AT91C_BASE_AIC->AIC_EOICR = 0;                               /* End of ISR */

}

In the main routine I ask if the flag is set -> if it is set, I use printf() to see via the usart that I get a new frame...

printf("h");

The problem is that I always get a data abort within the printf() routine..

Could it be that the ethernet frames came to close together (in a short time) so that the printf() routine has not finish to transmit the last character?

And after the ISR the printf() routine don't know what to do / what to send -> data abort??

If the printf-argument is much bigger - it is often so that I only can transmit one or two letter - but not the whole word...

e.g.

printf("hello world\n");

I only transmit "hel" - that's all...

best regards
Johannes

  • could there be something wrong with the heap-size? retarget should use the heap - I've nothing changed in the startup-file from Keil.

  • OR could there be a problem with the usart implementation - I always get a data abort during waiting for a empty TX buffer.

    int COM1_1_sendchar(int ch)
    {
            if (ch == '\n')  {                              /* Check for CR */
            while (!(COM1_1->US_CSR & AT91C_US_TXRDY));      /* Wait for Empty Tx Buffer */
            COM1_1->US_THR = '\r';                       /* Output CR */
    
            }
            while (!(COM1_1->US_CSR & AT91C_US_TXRDY));      /* Wait for Empty Tx Buffer */
            return (COM1_1->US_THR = ch);                        /* Transmit Character */
    }
    

    best regards
    Johannes

  • It seems like the COM1_1 pointer might be corrupted.

    Ussualy the practise with data aborts is to debug the program and find the instruction which caused the data abort.

    This can be done for example by stopping the CPU in data abort and finding the instruction (via LR) which caused it.

    Check which memory was accessed from the problematic instruction and you should be able to see the problem.

  • I define the COM1_1 ptr as

    AT91PS_USART COM1_1 = AT91C_BASE_US1;
    

    so it's the base address of the first usart.

    When I run in debug mode the data abort happens always within the function COM1_1_sendchar(int ch).

    int COM1_1_sendchar(int ch)
    {
            if (ch == '\n')  {                              /* Check for CR */
            while (!(COM1_1->US_CSR & AT91C_US_TXRDY));
                         //<-- data abort
            COM1_1->US_THR = '\r';
            }
            while (!(COM1_1->US_CSR & AT91C_US_TXRDY));      /* Wait for Empty Tx Buffer */
            return (COM1_1->US_THR = ch);                        /* Transmit Character */
    }
    

    I have installed another printf("hello") - bevor I enabled the interrupts for the ethernet - and the usart works very fine... so I don't think there could be a problem with the COM1_1 ptr.

    best regards
    Johannes

  • Insert a check in the COM1_1_sendchar() function to verify the pointer. If not as expected, insert a dummy instruction and place a breakpoint on it.

    You are most probably accessing unaligned or nonexisting memory, or trying to write within your flash address space.

  • I installed a variable i and insert a dummy instruction.

    Which register show me if I use nonexisting memory?

    R14(LR) = 0x10000890

    I have a external flash memory (16MB) from the base address 0x10000000.

    best regards
    Johannes

  • Just write something like:

    if (COM1_1 != AT91C_BASE_US1) {
        dummy_instruction;
    }
    

    And then set a breakpoint inside the if statement. If something has overwritten the COM1_1 pointer, then the debugger will stop at the breakpoint - before the data abort trap happens.

  • now I wrote:

    int COM1_1_sendchar(int ch)
    {
            unsigned int i=0;
    
            if (ch == '\n')  {                              /* Check for CR */
            while (!(COM1_1->US_CSR & AT91C_US_TXRDY));      /* Wait for Empty Tx Buffer */
            COM1_1->US_THR = '\r';
    
            if (COM1_1 != AT91C_BASE_US1) {
                    i++;
    }
    
            }
            while (!(COM1_1->US_CSR & AT91C_US_TXRDY));      /* Wait for Empty Tx Buffer */
            return (COM1_1->US_THR = ch);                        /* Transmit Character */
    }
    

    But the if-statement is never true - so I still have a data abort but the breakpoint (i++) was never reached.

    Johannes

  • Didn't you show the failure point a couple of lines before the point where you added the test?

    Do you think you should test if the pointer is valid before - or after - the point where you know that your application fails?

    What do you think your probabilities are of reaching this if statement, if the application has already failed?

  • code:

    init_usart();
    init_timer();
    init_ethernet();
    printf("ethernet init done\n"); //<- works

    while(1)
    { if(Emac_Receive >=1) // flag-bit set in ISR ethernet { printf("new frame\n"); //<- problem

    Emac_Receive--; }
    }

    Johannes

  • now I reach the point that the ptr is invalid

    int COM1_1_sendchar(int ch)
    {
            unsigned int i=0;
    
            if (ch == '\n')  {                              /* Check for CR */
            while (!(COM1_1->US_CSR & AT91C_US_TXRDY))       /* Wait for Empty Tx Buffer */
            {
                            if (COM1_1 != AT91C_BASE_US1) {
                    i++;
                            }
                    COM1_1->US_THR = '\r';
    
    
    }
    

    But how can I see which instructions caused this data abort

    R14(LR) 0x10000898 -> I type into the output window u 0x10000890 -->

     int fputc(int ch. FILE *f) {
          return (COM1_1_sendchar(ch));
          data abort --> //
    

    0x10000890 E1A00004 MOV R0, R4

    Johannes

  • If your ARM supports a data-change breakpoint, then set such a breakpoint on the pointer and then run the program.

    If it doesn't, then you have to figure this out manually. Is the pointer a global variable? Are there any array variables with lower address, so that a overrun will kill following variables?

    Where is the stack in relation to this pointer? Wíll the stack grow into your variables?

    Add test code to verify your pointer at a number of places in your program and run. Can you figure out that it always gets destroyed between two points, then narrow it down.

    Or, it may be an interrupt handler that breaks it.

    In other worlds, normal elbow grease...

  • Your goal is to figure out why your pointer is invalid, not where the data abort happens.

    You don't care about which instruction that generates a data abort, since you already know that the data abort is caused by an invalid pointer. In short: Valid code is more or less expected to generate a data abort if you fail to fulfill the required preconditions.

  • Is the pointer a global variable?

    Yes the pointer COM1_1 is a global variable so that I can reach it from the init_usart function and the COM1_1_sendchar() function.

    Where is the stack in relation to this pointer? Wíll the stack grow into your variables?

    Where can I see how big the stack is within the programm runs? Or if a overflow occured?

    Johannes

  • You are reading the documentation for the Keil compiler, and the documentation for your processor? As I said, it is time for some elbow grease - yours, not mine.