Wednesday, July 27, 2011

Data read from Kinetis K60 Flash is inconsistent

Problem:
When data is written to the Flash in the Kinetis K60 controller and the data is read through the program, the read data differs from the actual data written to the Flash. But, the data is intact when reading through the debugger. The contents of the flash memory seems to be corrupted/inconsistent.

Solution:
Though there is no separate cache in Kinetis K60, the Flash alone has prefetch functionality enabled and caching enabled. This causes this problem. To make sure, disable all cache, prefetch and single entry buffer as follows:


    uint32 temp_reg;

    temp_reg = FMC_PFAPR;    /* store present value of FMC_PFAPR */
    FMC_PFAPR = 0x00ff0000; /* Disable prefetch temporarily */

    FMC_PFB0CR &= ~0x00080000;
    FMC_PFB1CR &=  ~0x00080000;

    FMC_PFB0CR &= ~0x0000001f;
    FMC_PFB1CR &= ~0x0000001f;

    FMC_PFAPR = temp_reg;    /* re-store original value of FMC_PFAPR */

Have great embedded computing!

"Data TLB access error" exception when XMD(GDB) resumes execution from breakpoint


After breakpoint is set up in Xilinx XMD(GDB) debugger and the program is run to the breakpoint, when trying to continue/resume the execution or step through the code, "Data TLB access error" exception occurs.

This problem indicates stack corruption or uninitialized initial stack frame. When program execution reaches a breakpoint, execution pauses and gdb begins to reconstruct the backtrace. That means, using the contents of stack, the debugger trace through the previous stack frames and lists the calling functions corresponding to each stack-frame.

Actually, each stack-frame contains the (back chain pointer, which is) pointer to the previously allocated stack-frame and the pointer to the calling function.

GDB(XMD) continues to trace until the backchain pointer for a stack-frame is 0x00000000 which is the initial stack frame corresponding to the start of the program.

Therefore, either if the back-chain pointer of the initial stack frame is not initialized to NULL(0x00000000) by the program or if stack is corrupted, invalid addresses might be referred during the back-trace and "Data TLB access error" exception occurs when resuming the execution.

For more details about GDB backtrace, please refer to the following:
http://devpit.org/wiki/GDB

Have a nice debug!

Monday, July 25, 2011

Xilinx TEMAC checksum offload verification: Optimized solution

XPS LL TEMAC core calculates raw checksum over the entire Ethernet payload. According to the product specification, to verify the checksum, the checksum of the fields which should not have been included must be subtracted from the raw checksum and the adjusted raw checksum has to be compared with the checksum field of the TCP or UDP header.

But the above mentioned method is a time-consuming solution and I found a more optimized solution by which, the raw checksum is compared with the checksum of the fields which should not have been included. If both are equal, the checksum verification is passed. Otherwise, it is failed.

How is it possible?

Raw checksum = Checksum of TCP/UDP payload + Checksum field of TCP/UDP packet + Checksum of fields that should not have been included

For valid packet, the whole checksum(including the checksum field) of TCP/UDP packet is zero. So,

Raw checksum = 0 + Checksum of fields that should not have been included

Raw checksum = Checksum of fields that should not have been included

Pseudo code for the solution


bool temac_verify_csum(unsigned char *pkt_buf, unsigned short rx_csraw)
{
    unsigned short *temp;
    unsigned int csum;


    if(ntohs(*(unsigned short *)(pkt_buf + 12)) == 0x0800)/* IPv4 */
    {
        struct ip_header *ip_hdr = (struct ip_header *)pkt_buf + 14;
        temp = (unsigned short *)ip_hdr;
     
        /* Other than TCP/UDP, pass to the upper layer */
        if(ip_hdr->protocol != UDP && ip_hdr->protocol != TCP)
            return PASSED;


        /* If fragment, pass it to upper layer */
        if(ip_hdr->fragment)
            return PASSED;


        /* UDP with checksum 0. No need to verify */
        if(ip_hdr->protocol == UDP && *(temp + 13) == 0)
            return PASSED;


        /* Add the fields that should not be included */
        csum = *temp++;      /* Version, IHL, Differentiated Services */
        csum += ip_header_length; /* 20 */
        temp++;
        csum += *temp++; /* Identification */
        csum += *temp++; /* Flags and Fragment offset */
        csum += *temp++ & htons(0xFF00); /* TTL */
        csum += *temp;   /* IP header checksum */
    }
    else if(ntohs(*(unsigned short *)(pkt_buf + 12)) == 0x86dd)/* IPv6 */
    {
        struct ip6_header *ip6_hdr = (struct ip6_header *)pkt_buf + 14;
        temp = (unsigned short *)ip6_hdr;


        if(ip6_hdr->next_header != TCP && ip6_next_header != UDP)
            return PASSED;
        if(ip6_hdr->next_header == UDP && *(temp + 23) == 0)
            return PASSED;/* UDP with checksum 0. No need to verify */
        csum = *temp++;   /* Version, Traffic class */
        csum += *temp++;  /* and Flow Label */
        temp++;
        csum += *temp++;  /* Next header and Hop Limit */
        /* Next header must be included. So, minus it */
        csum -= htons(ip6_hdr->next_header);
    }
    else
        return PASSED;


    csum = (csum & 0xFFFF) + (csum >> 16);
    csum += (csum >> 16);
    if((unsigned short)csum == rx_csraw)
        return PASSED;
    return FAILED;
}


Where rx_csraw is raw checksum in the descriptor and pkt_buf is pointer to the buffer of the descriptor. Surely, it is an optimized solution, right?

For details on checksum offload of transmission side, look at the following post:
http://embeddedknowledge.blogspot.com/2009/08/xilinx-temac-checksum-offload.html

If you have any queries, write them as comments.