Wednesday, January 11, 2012

How to test cache invalidate and flush routines

Sometimes it is difficult to debug Cache related problems since the Cache contents are not exposed explicitly to the debugger. For example, in a LAN driver, though the DMA transfers the data and data length, they may not be reflected when reading the Buffer Descriptor. If the buffer descriptors are located in Cached memory area, we can guess there is problem in invalidating the cache corresponding to th at memory area . But, how to check that there is no problem with cache invalidate function? The following routines will give the solutions to test the cache invalidation and cache flush functionality.

/* Buffer for Test */
#define BUF_LEN 128
volatile unsigned int buffer[BUF_LEN];

/* User implemented functions */
extern void _data_cache_disable(void);
extern void _data_cache_enable(void);
extern void _data_cache_invalidate(void *, unsigned int);
extern void _data_cache_flush(void *,  unsigned int);

int test_invalidate()
{
    int i;

    /* Enable cache */
    _data_cache_enable();
    /* Write into cache */
    for (i = 0; i < BUF_LEN; i++)
        buffer[i] = (unsigned int)(buffer + i);
    /* Disable cache */
    _data_cache_disable();
    /* Write into memory */
    for (i = 0; i < BUF_LEN; i++)
        buffer[i] = 0xdeadbeef + i;
    /* Enable cache */
    _data_cache_enable();
    /* Compare cached data */
    for (i = 0; i < BUF_LEN; i++)
        if (buffer[i] != (unsigned int)(buffer + i)) {
            _data_cache_disable();
            return -1; /* Not cached */
        }
    /* Invalidate the cache */
    _data_cache_invalidate((void *)buffer, BUF_LEN*4);
    /* Compare data in memory */
    for (i = 0; i < BUF_LEN; i++)
        if (buffer[i] != (0xdeadbeef + i)) {
            _data_cache_disable();
            return 0; /* Cache is not invalidated */
        }
    /* Disable cache */
    _data_cache_disable();
    return 1;
}

int test_flush()
{
    int i;

    /* Enable cache */
    _data_cache_enable();
    /* Write into cache */
    for (i = 0; i < BUF_LEN; i++)
        buffer[i] = (unsigned int)(buffer + i);
    /* Disable cache */
    _data_cache_disable();
    /* Write into memory */
    for (i = 0; i < BUF_LEN; i++)
        buffer[i] = 0xdeadbeef + i;
    /* Enable cache */
    _data_cache_enable();
    /* Compare cached data */
    for (i = 0; i < BUF_LEN; i++)
        if(buffer[i] != (unsigned int)(buffer + i)) {
            _data_cache_disable();
            return -1; /* Not cached */
        }
    /* Flush the cache */
    _data_cache_flush((void *)buffer, BUF_LEN*4);
    /* Disable cache */
    _data_cache_disable();
    /* Compare data in memory */
    for (i = 0; i < BUF_LEN; i++)
        if(buffer[i] != (unsigned int)(buffer + i))
            return 0; /* Cache is not flushed */
    return 1;
}

/* Sample Test main program */
int main()
{
    int errno;

    errno= test_invalidate();
    if (errno== 1)
        printf("Successfully Invalidated");     /* Success */
    else if (errno== 0)
        printf("Some data is not invalidated"); /* Failed */
    else
        printf("Data is not at all cached");    /* Not cached */

    errno= test_flush();
    if (errno== 1)
        printf("Successfully flushed");         /* Success */
    else if (errno== 0)
         printf("Some data is not flushed");    /* Failed */
    else
         printf("Data is not at all cached");   /* Not cached */
}

The above program tests cache invalidate and cache flush with following conditions provided:
_data_cache_disable - Just disables the cache without affecting the contents
_data_cache_enable - Just enables the cache without affecting the contents

When the test failed, check the cache size and cache line length. When the data is not cached, check whether the cache is enabled.