/*
 * $Header: /projects/raw/cvsroot/benchmark/suites/des/src/driver.c,v 1.6 1997/08/15 18:36:15 jbabb Exp $
 *
 * Driver for DES benchmark
 *
 * Authors: Victor W. K. Lee        (wklee@lcs.mit.edu)
 *          Jonathan Babb           (jbabb@lcs.mit.edu)
 *
 * Copyright @ 1997 MIT Laboratory for Computer Science, Cambridge, MA 02129
 */

#include <copyright.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "des.h"

#include <generate_verilog.h>
#include <tval.h>
#include <util.h>

#ifdef HARDWARE
#include <interface.h>
#endif

#define DATA      0
#define KEY       1
#define START     2
#define STATUS    3
#define MEMSPACE  2


#define CONTROLID 0
#define SCANID    1


#define ITER      1


/* The following set of macros configures the driver
 * according to the mode we are running in:
 *
 * -DSOFTWARE    a software-only version of the benchmark.
 * -DSIMULATION  generates simulation vectors to a verilog file.
 * -DHARDWARE    runs reconfigurable hardware via a memory-mapped interface.
 *
 * The modes can be -DHARDWARE, SIMULATION, and SOFTWARE.
 *
 * The intRead, intWrite, intClose and intOpen calls
 * are defined for a generic interface for both the
 * HARDWARE and SIMULATION modules. They map to the corresponding
 * functions:
 * 
 *
 * Simulation:
 * (generate_verilog.h)
 *
 *   vectorRead  - generate a verilog read vector for the test shell
 *   vectorWrite - generate a verilog write vector for the test shell
 *   vectorNop   - generate a verilog nop vector for the test shell
 *
 * Hardware:
 * (interface.h)
 *
 *   interfaceOpen  - open the memory mapped SBus to VLE interface
 *   interfaceClose - close the memory mapped SBus to VLE interface
 *   interfaceRead  - a macro to read from the interface
 *   interfaceWrite - a macro to write to the interface
 *
 */

/* An additional flag -DHARDWARE_ONLY (currently unused) turns off execution
   and comparison of the software version whenever the hardware is driven */


#ifdef HARDWARE

/* #define driver DriverNotSoftware */
char* gDriverMode="hardware";

#define intWrite(base, index, value) interfaceWrite(base + index, value)
#define intRead(base, index, value) interfaceRead(base + index, value)
#define intOpen(x) interfaceOpen(x)
#define intClose(x) interfaceClose()
#define intNoop(x) 

#else

#ifdef SIMULATION

#include <stdio.h>

/* #define driver DriverNotSoftware */
char* gDriverMode="simulation";

#define intWrite(base, index, value) vectorWrite(index, value)
#define intRead(base, index, value) vectorRead(index, value)
#define intOpen(x)
#define intClose()
#define intNoop(x) vectorNop(x)

#else

#ifdef SOFTWARE
/* #define driver DriverSoftware */
char* gDriverMode="software";

#else

#error One of HARDWARE, SIMULATION, or SOFTWARE must be defined.
#endif /* software */
#endif /* simulation */
#endif /* hardware */


/* The hardware DES routine takes 18 HW cycles. */

#define CYCLES 18


/* Global Data Structure holding test data */

#define NUM_TESTS 34

static unsigned char key_data[NUM_TESTS][8]={
	{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
	{0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF},
	{0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
	{0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11},
	{0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF},
	{0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11},
	{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
	{0xFE,0xDC,0xBA,0x98,0x76,0x54,0x32,0x10},
	{0x7C,0xA1,0x10,0x45,0x4A,0x1A,0x6E,0x57},
	{0x01,0x31,0xD9,0x61,0x9D,0xC1,0x37,0x6E},
	{0x07,0xA1,0x13,0x3E,0x4A,0x0B,0x26,0x86},
	{0x38,0x49,0x67,0x4C,0x26,0x02,0x31,0x9E},
	{0x04,0xB9,0x15,0xBA,0x43,0xFE,0xB5,0xB6},
	{0x01,0x13,0xB9,0x70,0xFD,0x34,0xF2,0xCE},
	{0x01,0x70,0xF1,0x75,0x46,0x8F,0xB5,0xE6},
	{0x43,0x29,0x7F,0xAD,0x38,0xE3,0x73,0xFE},
	{0x07,0xA7,0x13,0x70,0x45,0xDA,0x2A,0x16},
	{0x04,0x68,0x91,0x04,0xC2,0xFD,0x3B,0x2F},
	{0x37,0xD0,0x6B,0xB5,0x16,0xCB,0x75,0x46},
	{0x1F,0x08,0x26,0x0D,0x1A,0xC2,0x46,0x5E},
	{0x58,0x40,0x23,0x64,0x1A,0xBA,0x61,0x76},
	{0x02,0x58,0x16,0x16,0x46,0x29,0xB0,0x07},
	{0x49,0x79,0x3E,0xBC,0x79,0xB3,0x25,0x8F},
	{0x4F,0xB0,0x5E,0x15,0x15,0xAB,0x73,0xA7},
	{0x49,0xE9,0x5D,0x6D,0x4C,0xA2,0x29,0xBF},
	{0x01,0x83,0x10,0xDC,0x40,0x9B,0x26,0xD6},
	{0x1C,0x58,0x7F,0x1C,0x13,0x92,0x4F,0xEF},
	{0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01},
	{0x1F,0x1F,0x1F,0x1F,0x0E,0x0E,0x0E,0x0E},
	{0xE0,0xFE,0xE0,0xFE,0xF1,0xFE,0xF1,0xFE},
	{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
	{0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF},
	{0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF},
	{0xFE,0xDC,0xBA,0x98,0x76,0x54,0x32,0x10}};

static unsigned char plain_data[NUM_TESTS][8]={
	{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
	{0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF},
	{0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x01},
	{0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11},
	{0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11},
	{0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF},
	{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
	{0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF},
	{0x01,0xA1,0xD6,0xD0,0x39,0x77,0x67,0x42},
	{0x5C,0xD5,0x4C,0xA8,0x3D,0xEF,0x57,0xDA},
	{0x02,0x48,0xD4,0x38,0x06,0xF6,0x71,0x72},
	{0x51,0x45,0x4B,0x58,0x2D,0xDF,0x44,0x0A},
	{0x42,0xFD,0x44,0x30,0x59,0x57,0x7F,0xA2},
	{0x05,0x9B,0x5E,0x08,0x51,0xCF,0x14,0x3A},
	{0x07,0x56,0xD8,0xE0,0x77,0x47,0x61,0xD2},
	{0x76,0x25,0x14,0xB8,0x29,0xBF,0x48,0x6A},
	{0x3B,0xDD,0x11,0x90,0x49,0x37,0x28,0x02},
	{0x26,0x95,0x5F,0x68,0x35,0xAF,0x60,0x9A},
	{0x16,0x4D,0x5E,0x40,0x4F,0x27,0x52,0x32},
	{0x6B,0x05,0x6E,0x18,0x75,0x9F,0x5C,0xCA},
	{0x00,0x4B,0xD6,0xEF,0x09,0x17,0x60,0x62},
	{0x48,0x0D,0x39,0x00,0x6E,0xE7,0x62,0xF2},
	{0x43,0x75,0x40,0xC8,0x69,0x8F,0x3C,0xFA},
	{0x07,0x2D,0x43,0xA0,0x77,0x07,0x52,0x92},
	{0x02,0xFE,0x55,0x77,0x81,0x17,0xF1,0x2A},
	{0x1D,0x9D,0x5C,0x50,0x18,0xF7,0x28,0xC2},
	{0x30,0x55,0x32,0x28,0x6D,0x6F,0x29,0x5A},
	{0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF},
	{0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF},
	{0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF},
	{0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF},
	{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
	{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
	{0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}};

static unsigned char cipher_data[NUM_TESTS][8]={
	{0x8C,0xA6,0x4D,0xE9,0xC1,0xB1,0x23,0xA7},
	{0x73,0x59,0xB2,0x16,0x3E,0x4E,0xDC,0x58},
	{0x95,0x8E,0x6E,0x62,0x7A,0x05,0x55,0x7B},
	{0xF4,0x03,0x79,0xAB,0x9E,0x0E,0xC5,0x33},
	{0x17,0x66,0x8D,0xFC,0x72,0x92,0x53,0x2D},
	{0x8A,0x5A,0xE1,0xF8,0x1A,0xB8,0xF2,0xDD},
	{0x8C,0xA6,0x4D,0xE9,0xC1,0xB1,0x23,0xA7},
	{0xED,0x39,0xD9,0x50,0xFA,0x74,0xBC,0xC4},
	{0x69,0x0F,0x5B,0x0D,0x9A,0x26,0x93,0x9B},
	{0x7A,0x38,0x9D,0x10,0x35,0x4B,0xD2,0x71},
	{0x86,0x8E,0xBB,0x51,0xCA,0xB4,0x59,0x9A},
	{0x71,0x78,0x87,0x6E,0x01,0xF1,0x9B,0x2A},
	{0xAF,0x37,0xFB,0x42,0x1F,0x8C,0x40,0x95},
	{0x86,0xA5,0x60,0xF1,0x0E,0xC6,0xD8,0x5B},
	{0x0C,0xD3,0xDA,0x02,0x00,0x21,0xDC,0x09},
	{0xEA,0x67,0x6B,0x2C,0xB7,0xDB,0x2B,0x7A},
	{0xDF,0xD6,0x4A,0x81,0x5C,0xAF,0x1A,0x0F},
	{0x5C,0x51,0x3C,0x9C,0x48,0x86,0xC0,0x88},
	{0x0A,0x2A,0xEE,0xAE,0x3F,0xF4,0xAB,0x77},
	{0xEF,0x1B,0xF0,0x3E,0x5D,0xFA,0x57,0x5A},
	{0x88,0xBF,0x0D,0xB6,0xD7,0x0D,0xEE,0x56},
	{0xA1,0xF9,0x91,0x55,0x41,0x02,0x0B,0x56},
	{0x6F,0xBF,0x1C,0xAF,0xCF,0xFD,0x05,0x56},
	{0x2F,0x22,0xE4,0x9B,0xAB,0x7C,0xA1,0xAC},
	{0x5A,0x6B,0x61,0x2C,0xC2,0x6C,0xCE,0x4A},
	{0x5F,0x4C,0x03,0x8E,0xD1,0x2B,0x2E,0x41},
	{0x63,0xFA,0xC0,0xD0,0x34,0xD9,0xF7,0x93},
	{0x61,0x7B,0x3A,0x0C,0xE8,0xF0,0x71,0x00},
	{0xDB,0x95,0x86,0x05,0xF8,0xC8,0xC6,0x06},
	{0xED,0xBF,0xD1,0xC6,0x6C,0x29,0xCC,0xC7},
	{0x35,0x55,0x50,0xB2,0x15,0x0E,0x24,0x51},
	{0xCA,0xAA,0xAF,0x4D,0xEA,0xF1,0xDB,0xAE},
	{0xD5,0xD4,0x4F,0xF7,0x20,0x68,0x3D,0x0D},
	{0x2A,0x2B,0xB0,0x08,0xDF,0x97,0xC2,0xF2}};


/*
 * The main driver for testing. It has three modes.  
 * SOFTWARE to run the software mode.
 * SIMULATION to run the simulation mode.
 * HARDWARE to run the hardware mode.
 */


driver(int size, int scan)
{
  unsigned int maxpoll, cycles=CYCLES, width=64, data;
  unsigned int addr;
  unsigned int key_hi, key_lo, data_hi, data_lo, out_hi, out_lo;
  unsigned long * base;
  int rpts=0, i, j, k, test, total_num_tests, base_addr;
  char encrypted_output [NUM_TESTS][8];
  char decrypted_output [NUM_TESTS][8];
  float soft_time;
  des_cblock in,out,outin;
  des_key_schedule ks;

  tval_t begin_time, end_time;

  /* Initialize the output array */

  memset(encrypted_output,0,(NUM_TESTS*8));
  memset(decrypted_output,0,(NUM_TESTS*8));
  
#ifdef HARDWARE
  interfaceOpen(&base);
#endif

#ifndef HARDWARE_ONLY

  /*
   * In this version, we simplify the test to only doing DES on
   * one element. 
   *
   * The software driver uses Eric Young's des_ecb_encrypt routine
   * to perform simple ecb encryption.
   */


  /* Set the key */
  
  if ((des_key_sched((C_Block *) key_data[0],ks)) != 0) {
    printf("Key error!\n");
    exit(-1);
  }
  
  
  /* Run the encrytion loop */
  
#ifdef SOFTWARE
  get_time(begin_time);

  do {
    rpts++;
#endif

  for (k=0; k<size*ITER; k++) {
    memcpy(in,plain_data[0],8);
    memset(out,0,8);
    des_ecb_encrypt((C_Block *)in,(C_Block *)out,ks,DES_ENCRYPT);
    if (memcmp(cipher_data[0],out,8) != 0) {
      printf("Encryption error %d\n", k);
      exit(-1);
    }

    /* 
       printf("output: %x %x %x %x %x %x %x %x\n", 
       out[0], out[1], out[2], out[3], 
       out[4], out[5], out[6], out[7]); 
    */

  }

#ifdef SOFTWARE
  } while (get_time(end_time) - begin_time < TIMELIMIT);
#endif

#endif

#ifndef SOFTWARE


  /* generate key */

  key_hi = key_data[0][0] << 24 | key_data[0][1] << 16 |
           key_data[0][2] << 8  | key_data[0][3];
  key_lo = key_data[0][4] << 24 | key_data[0][5] << 16 |
           key_data[0][6] << 8  | key_data[0][7];
      

  /* write the key to the hardware/simulator */

  addr = CONTROLID << MEMSPACE | KEY;
  intWrite(base, addr, key_lo);
  addr = CONTROLID << MEMSPACE | KEY;
  intWrite(base, addr, key_hi);


  for (i=0; i<ITER; i = i + size) {


    /* Write data and start */


    for (j=1; j<=size; j++) {


      /* setup the data */

      data_hi = plain_data[0][0] << 24 | plain_data[0][1] << 16 |
	        plain_data[0][2] << 8  | plain_data[0][3];
      data_lo = plain_data[0][4] << 24 | plain_data[0][5] << 16 |
	        plain_data[0][6] << 8  | plain_data[0][7];


      /* write the data to the hardware/simulator */

      base_addr = scan ? SCANID : j;

      addr = base_addr << MEMSPACE | DATA;
      intWrite(base, addr, data_lo);

      addr = base_addr << MEMSPACE | DATA;
      intWrite(base, addr, data_hi);
    }
 

    /* Starting the DES */

    addr = CONTROLID << MEMSPACE | START;
    intWrite(base, addr, 1);


    /* now poll and read back the data from the hardware/simulator
       if the number of iteration is more than the hardware size */

#ifdef SIMULATION


    intNoop(cycles);


    /* now expect an "1" from polling the status address */

    addr = CONTROLID << MEMSPACE | STATUS;

    data = 1;
    intRead(base, addr, data);


    /* Now read and check the data */

    /* printf("encrypted_output[%d]: %x %x %x %x %x %x %x %x\n", test,
	 encrypted_output[test][0], encrypted_output[test][1], 
	 encrypted_output[test][2], encrypted_output[test][3], 
	 encrypted_output[test][4], encrypted_output[test][5], 
	 encrypted_output[test][6], encrypted_output[test][7]); */

    out_hi = (cipher_data[0][0] & 0xff) << 24 | 
             (cipher_data[0][1] & 0xff) << 16 |
	     (cipher_data[0][2] & 0xff) <<  8 | 
	     (cipher_data[0][3] & 0xff);     

    out_lo = (cipher_data[0][4] & 0xff) << 24 | 
	     (cipher_data[0][5] & 0xff) << 16 |
	     (cipher_data[0][6] & 0xff) <<  8 | 
	     (cipher_data[0][7] & 0xff);     

    for (j=1; j<=size; j++) {

      base_addr = scan ? SCANID : j;
      addr = base_addr << MEMSPACE | DATA;
      intRead(base, addr, out_lo);

      /* printf("data_lo: %x",out_lo); */


      base_addr = scan ? SCANID : j;
      addr = base_addr << MEMSPACE | DATA;
      intRead(base, addr, out_hi);

      /* printf("data_hi: %x",out_hi); */
	
    }
#endif

#ifdef HARDWARE	


    /* polling to see if the hardware has finished */

    addr = CONTROLID << MEMSPACE | STATUS;
    
    while(data!=1 && maxpoll < MAXPOLL) {
      maxpoll ++;
      intRead(base, addr, data);
    }
	
    if(maxpoll==MAXPOLL) {
      printf("Error: exceeding polling limit %d.\n",MAXPOLL);
    }

    out_hi = (cipher_data[0][0] & 0xff) << 24 | 
	     (cipher_data[0][1] & 0xff) << 16 |
	     (cipher_data[0][2] & 0xff) <<  8 | 
	     (cipher_data[0][3] & 0xff);     

    out_lo = (cipher_data[0][4] & 0xff) << 24 | 
	     (cipher_data[0][5] & 0xff) << 16 |
	     (cipher_data[0][6] & 0xff) <<  8 | 
	     (cipher_data[0][7] & 0xff);     

    for (j=1; j<=size; j++) {

      base_addr = scan ? SCANID : j;
      addr = base_addr << MEMSPACE | DATA;
      intRead(base, addr, data);

      encrypted_output[0][4] = data >> 24 & 0xff;
      encrypted_output[0][5] = data >> 16 & 0xff;
      encrypted_output[0][6] = data >> 8  & 0xff;
      encrypted_output[0][7] = data       & 0xff;
      
      base_addr = scan ? SCANID : j;
      addr = base_addr << MEMSPACE | DATA;
      intRead(base, addr, data);

      encrypted_output[0][0] = data >> 24 & 0xff;
      encrypted_output[0][1] = data >> 16 & 0xff;
      encrypted_output[0][2] = data >> 8  & 0xff;
      encrypted_output[0][3] = data       & 0xff;
      
      if (memcmp(cipher_data[0],encrypted_output[0],8) != 0) {
	printf("HW encryption error %2d\n", j);
      }
    }
    
#endif
  }
#endif


#ifdef SOFTWARE
  print_array((u_int *) out, 2);
  soft_time=diff_time(end_time, begin_time) / ((float)rpts);
  report_results(size,width,cycles,soft_time);
#endif


#ifdef HARDWARE

#ifndef HARDWARE_ONLY
  compare_array((u_int *) encrypted_output,(u_int *) out, 2);
#else
  print_array((u_int *) encrypted_output, 2);
#endif

  interfaceClose();
#endif

}


/*
  Main routine for driver. Usage and input parameters are
  specific to this benchmark. Modify accordingly to support
  your benchmark.
*/

main(int argc, char** argv)
{
  int size, scan;

  if (argc != 3) {
    printf("Driver for Integer MatrixMultiply benchmark.\n"
	   "\n"
	   "usage: driver-%s <size> <scan>\n"
	   "\n"
	   "size is the number of processing node for hardware version \n"
	   "scan is either scan or bus for respective implementation     \n"
	   "\n",gDriverMode);
    exit(-1);
  };

  /* parameters */

  size=atoi(argv[1]);
  scan=strcmp(argv[2], "scan") == 0; /* 0 for bus, 1 for scan */

  driver(size,scan);

  exit(0);
}
