/*
 * TLB stress tests.
 *
 * Allocate 1024 pages, and run around on them.
 *
 * Compile with -DTIMES to get some timing information.
 */

#include <stdlib.h>
#include <stdio.h>
#include <malloc.h>
#include <time.h>

#define Kb		* 1024
#define PAGE_SIZE	(16 Kb)

#define E(x)		\
   if ((x) == NULL) { fprintf(stderr, #x " = NULL\n"); exit(1); }
#define EXPECT32(x, y)	\
   if ((x) != (y))  {	\
      fprintf(stderr, "EXPECT32(0x%08x, 0x%08x)\n", (x), (y));	\
      ++expect_errors;						\
   }
#ifdef TIMES
# define T(x)		\
   {								\
      clock_t c = clock();					\
      x;							\
      printf(#x ":\t\t%.2gs\n", (double) (clock() - c)/CLOCKS_PER_SEC);\
   }
#else
# define T(x)
#endif

#ifdef VERBOSE
# define V(args...)	printf(args)
#else
# define V(args...)
#endif

static int mem_init       (void* mem, size_t mem_size);
static int access_linear  (void* mem, size_t mem_size);
static int access_pagewise(void* mem, size_t mem_size);
static int access_random  (void* mem, size_t mem_size);

static int expect_errors;

int main( int argc, char* argv[] )
{
   size_t mem_size = 1024 * PAGE_SIZE;
   void*  mem;

   E( mem = memalign(PAGE_SIZE, mem_size) );

   V( "mem=[%p, %p)\n", mem, mem + mem_size );

   T( mem_init       (mem, mem_size) );
   T( access_linear  (mem, mem_size) );
   T( access_pagewise(mem, mem_size) );
   T( access_random  (mem, mem_size) );

   return 0;
}


/*
 * Every word is initialised with (PAGE_NUMBER << 16 | OFFSET_IN_PAGE)
 */

static int mem_init( void* mem, size_t size )
{
   unsigned int* p = mem;
   int           i;

   V( "mem_init: %d pages...", size/PAGE_SIZE );

   for ( i = 0; i < size/4; i++  )
      p[i] = (4*i) / PAGE_SIZE << 16 | (4*i) % PAGE_SIZE;

   V( " done\n" );

   return 0;
}

static int access_linear( void* mem, size_t size )
{
   unsigned int* p = mem;
   int           i;

   V( "access_linear: %d pages...", size/PAGE_SIZE );

   for ( i = 0; i < size/4; i++  )
      EXPECT32(p[i], (4*i) / PAGE_SIZE << 16 | (4*i) % PAGE_SIZE);

   V( " done\n" );

   return 0;
}

static int access_pagewise( void* mem, size_t size )
{
   unsigned int* p = mem;
   int           page, offset;

   V( "access_pagewise: %d pages...", size/PAGE_SIZE );

   for ( offset = 0; offset < PAGE_SIZE/4; offset++ )
   {
      for ( page = 0; page < size/PAGE_SIZE; page++ )
	 EXPECT32(p[page*PAGE_SIZE/4 + offset], page << 16 | 4*offset);
   }
   V( " done\n" );

   return 0;
}

static int access_random( void* mem, size_t size )
{
   unsigned int* p = mem;
   int           i,ii;
   V( "access_random: %d pages...", size/PAGE_SIZE );

   for ( ii = 0; ii < size/4; ii++  ) {
      i= random() & (PAGE_SIZE-1);
      EXPECT32(p[i], (4*i) / PAGE_SIZE << 16 | (4*i) % PAGE_SIZE);
   }

   V( " done\n" );

   return 0;
}
