/*********************** DESMIM Nios Controller (c) 2001 Michael Bond ****************/

/* Shell Type :  C Mk. IIb */
 
/* 
Filename : desmim.c
Created : 8th March 2001
Creators : Michael Bond
Format : C
Compilation : ???
Detail : the code for the nios controller onboard the Desmim FPGA

Version History (Defined as DATE VERSION REVISION SUBSIDIARY)

DATE            V.      R.      S.      INFO
30/04/01        1.0     1       0       Making rough outline of code
02/05/01        1.0     1       1       Filling in with some NIOS library routines
21/05/01		1.0		2		0		Integrating with INSTRUCT.V v1.0 r1.0
24/05/01		1.0		3		0		Integrating with destalk PC client
24/05/01		1.0		3		1		Debugging command set
24/05/01		1.0		4		0		Rewritten to use ascii for comms & is stable
27/05/01		1.0		4		1		more instruct.v integration
29/05/01		1.0		4		2		now with an 'xx' reset command to reset instruction decoder
11/06/01		1.0		5		0		now with checksummed commands
09/07/01		1.0		6		0		new command set


NIOS COMMAND SET
----------------

likewiseish... for the time being

*/


//************************************************************************************************* //
// ******************************** Inclusions  *************************************************** //
// ************************************************************************************************ //

#include "nios_map.h"
#include "nios_peripherals.h"
#include <string.h>

//************************************************************************************************* //
// ******************************** Definitions  ************************************************** //
// ************************************************************************************************ //

#define VERSION 1.0
#define REVISION 5.0

#define TRUE -1
#define FALSE 0

#define UCHAR unsigned char
#define UINT16 unsigned short

// DESMIM COMMAND SET
// ------------------
//
// 0x0002 Load LSB 16 bits
// 0x0003 Load MSB 16 bits
// 0x0004 Fetch LSB 16 bits
// 0x0005 Fetch MSB 16 bits
//
// 0x0008 Set LS 32 bits to encrypt
// 0x0009 Set MS 32 bits to encrypt
// 0x0010 Set 15 bit SRAM address
// 0x0020 Write to SRAM & Increment address
// 0x0040 Read from SRAM & Increment address
// 0x0080 Run crypto routine
// 0x0100 Fetch LS 32 bits of result
// 0x0101 Fetch MS 32 bits of result
// 0x0200 Fetch LS 32 bits of key
// 0x0201 Fetch MS 32 bits of key
//
// 0x0007 Reset
//
#define CMD_LOAD0                     0x002				 // engine is to latch LS 16 bit value
#define CMD_LOAD1                     0x003				 // engine is to latch MS 16 bit value
#define CMD_QUERY_FETCH0              0x004				 // engine is to present LS 16 bit value
#define CMD_QUERY_FETCH1              0x005				 // engine is to present MS 16 bit value

#define CMD_SET_PATTERN0			  0x008              // set ls 32 bit value for DES to encrypt
#define CMD_SET_PATTERN1			  0x009              // set ms 32 bit value for DES to encrypt

#define CMD_SET_SRAM_ADDR             0x010              // set access address for SRAM reads/writes

#define CMD_SRAM_WRITE_INC            0x020              // write a 32 bit value to SRAM & inc address
#define CMD_SRAM_READ_INC             0x040              // read a 32 bit value from SRAM & inc address

#define CMD_RUN                       0x080              // start the pipeline clock

#define CMD_GET_RESULT0				  0x100              // get DES encrypted result value (ls 32 bits)
#define CMD_GET_RESULT1				  0x101              // get DES encrypted result value (ms 32 bits)

#define CMD_GET_KEY0				  0x200				 // get DES key value (ls 32 bits)
#define CMD_GET_KEY1				  0x201			     // get DES key value (ms 32 bits)

#define CMD_CLEAR				      0x000				 // not a command
#define CMD_RESET                     0x00F				 // reset commmand

//#define CMD_SET_PIPELINE_COUNTER    0x00

#define CMD_QUERY_INTERNAL_GET_STATUS  0xFE

//-------------------- ascii version of command set ------------------

#define ASC_LOAD0                     (('l'<<8)+'0')		//"l0"
#define ASC_LOAD1                     (('l'<<8)+'1')		//"l1"

#define ASC_QUERY_FETCH0              (('f'<<8)+'0')		//"f0"
#define ASC_QUERY_FETCH1              (('f'<<8)+'1')		//"f1"

#define ASC_SRAM_WRITE_INC            (('s'<<8)+'l')		//"sl"
#define ASC_SRAM_READ_INC             (('s'<<8)+'f')		//"sf"
#define ASC_SET_SRAM_ADDR             (('s'<<8)+'a')		//"sa"

#define ASC_SET_TEST_PATTERN0         (('t'<<8)+'0')		//"t0"
#define ASC_SET_TEST_PATTERN1         (('t'<<8)+'1')		//"t1"

//#define ASC_SET_PIPELINE_COUNTER      (('p'<<8)+'l')		//"pl"

#define ASC_GET_KEY0				  (('k'<<8)+'0')		//"k0"               
#define ASC_GET_KEY1				  (('k'<<8)+'1')		//"k1"               

#define ASC_GET_RESULT0               (('r'<<8)+'0')		//"r0"               
#define ASC_GET_RESULT1               (('r'<<8)+'1')		//"r1"               

#define ASC_QUERY_GET_STATUS          (('g'<<8)+'s')		//"gs"

#define ASC_RUN                       (('r'<<8)+'r')		//"rr"

#define ASC_RESET                     (('x'<<8)+'x')		//"xx"

#define ASC_DEBUG					  (('y'<<8)+'y')		//"yy"

#define PIO_OUTPUT 0xFFFF		// all bits set for output
#define PIO_INPUT  0x0000		// all bits set for input

#define NO_CHAR -1

#define SUCCESS 0
#define ERROR_BAD_COMMAND 1
#define ERROR_BAD_CHECKSUM 2
#define ERROR 1

#define MAX_CMD 0x1000
#define MIN_CMD 0x0011

#define GENPOLY 0xA001
#define CRCINIT 0 // or 0xffff

//************************************************************************************************* //
// ************************************** Prototypes  ********************************************* //
// ************************************************************************************************ //

void send_command(int command, int param);
int send_query(int command);

UINT16 hex4char(char *inhex);
UCHAR hex1char(char c);

UINT16 crc_gen(const unsigned char *data, const UINT16 len);

//************************************************************************************************* //
// ***************************** Data Structures & Variables  ************************************* //
// ************************************************************************************************ //

char input[20];
      
//************************************************************************************************* //
// ********************************** Main Function *********************************************** //
// ************************************************************************************************ //

int main(int argc,char *argv[])
    {
    UINT16 data;
    
    int c;
	int ec=0;
	unsigned int count;	
	UINT16 crc_sent;
    
    UINT16 tcommand;
	UINT16 command;
	UINT16 nval;

	char reply[20];
	char rbuf[10];

	NIOS_GDB_SETUP

    // initialise

	printf("\nDESMIM Version 1.0 Revision 6.0\n\nREADY\n");

	// print squiggles
	//na_seven_seg_pio->np_piodirection=1;
	//na_seven_seg_pio->np_piodata=0xecec;

    send_command( CMD_RESET , 0 );           
    send_command( CMD_CLEAR , 0 );           

    while( TRUE )
        {
        // wait for characters from COM port

        while( (input[0]=nr_uart_rxchar(na_uart)) == NO_CHAR );
        while( (input[1]=nr_uart_rxchar(na_uart)) == NO_CHAR );
		while( (input[2]=nr_uart_rxchar(na_uart)) == NO_CHAR );
		while( (input[3]=nr_uart_rxchar(na_uart)) == NO_CHAR );
		while( (input[4]=nr_uart_rxchar(na_uart)) == NO_CHAR );
		while( (input[5]=nr_uart_rxchar(na_uart)) == NO_CHAR );
		
		input[6]='\0';

        tcommand=input[0] << 8;
        tcommand|=input[1];        		

		// calculate a checksum speculatively (this data may be load data, or a checksum)

		//printf("crc_sent (speculative) = %04X\r\n",crc_sent);
		
		//printf("input = '%s'\r\n",input);
		
        // display command on 7 segment display
        //nr_pio_showhex( ec++ );
		
		//na_seven_seg_pio->np_piodirection=1;			
		
		switch (tcommand)
		{
		case ASC_LOAD0:
			nval=(14 << 8) | 126;
			command=CMD_LOAD0;
			break;

		case ASC_LOAD1:
			nval=(14 << 8) | 48;			
			command=CMD_LOAD1;
			break;

		case ASC_QUERY_FETCH0:      
			nval=(71 << 8) | 126;			
			command=CMD_QUERY_FETCH0;
			break;

		case ASC_QUERY_FETCH1:      
			nval=(71 << 8) | 48;	
			command=CMD_QUERY_FETCH1;
			break;

		case ASC_SRAM_WRITE_INC:    
			nval=(91 << 8) | 14;	
			command=CMD_SRAM_WRITE_INC;
			break;

		case ASC_SRAM_READ_INC:
			nval=(91 << 8) | 71;	
			command=CMD_SRAM_READ_INC;
			break;

		case ASC_SET_SRAM_ADDR:
			nval=(91 << 8) | 119;			
			command=CMD_SET_SRAM_ADDR;
			break;

		case ASC_SET_TEST_PATTERN0: 
			nval=(112 << 8) | 126;			
			command=CMD_SET_PATTERN0;
			break;

		case ASC_SET_TEST_PATTERN1: 
			nval=(112 << 8) | 48;			
			command=CMD_SET_PATTERN1;
			break;

		/*************** THIS COMMAND (TEMPORARILY?) DISCONTINUED ***************
		case ASC_SET_PIPELINE_COUNTER:     
			nval=(103 << 8) | 14;		
			command=CMD_SET_PIPELINE_COUNTER;
			break;
		***************** END DISCONTINUE ***************************************/

		case ASC_GET_KEY0:     
			nval=(103 << 8) | 71;			
			command=CMD_GET_KEY0;
			break;

		case ASC_GET_KEY1:     
			nval=(103 << 8) | 71;			
			command=CMD_GET_KEY1;
			break;

		case ASC_GET_RESULT0: 
			nval=(103 << 8) | 71;			
			command=CMD_GET_RESULT0;
			break;

		case ASC_GET_RESULT1:     
			nval=(103 << 8) | 71;			
			command=CMD_GET_RESULT1;
			break;

		case ASC_RUN:
			nval=(128 << 8) | 128;			
			command=CMD_RUN;
			break;

		case ASC_RESET:
			nval=~(0xecec);
			command=CMD_RESET;
			break;

		case ASC_QUERY_GET_STATUS:
			nval=~(0xecec);
			command=CMD_QUERY_INTERNAL_GET_STATUS;
			break;

		case ASC_DEBUG:
			while (nr_uart_rxchar(na_uart) == NO_CHAR)
			{
				// debug loop for inspection with an oscilloscope

				send_command (CMD_RESET,          0     );
				
				send_command (CMD_LOAD1,          0x0000);
				send_command (CMD_LOAD0,          0x0000);
				send_command (CMD_SET_SRAM_ADDR,  0     );

				send_command (CMD_LOAD1,          0xDEAD);
				send_command (CMD_LOAD0,          0xBEEF);
				send_command (CMD_SRAM_WRITE_INC, 0     );
				send_command (CMD_LOAD1,          0x0123);
				send_command (CMD_LOAD0,          0x4567);
				send_command (CMD_SRAM_WRITE_INC, 0     );

				send_command (CMD_LOAD1,          0x0000);
				send_command (CMD_LOAD0,          0x0001);
				send_command (CMD_SET_SRAM_ADDR,  0     );

				send_command (CMD_SRAM_READ_INC,  0     );
				send_query   (CMD_QUERY_FETCH1);
				send_query   (CMD_QUERY_FETCH0);

				send_command (CMD_LOAD1,          0x0000);
				send_command (CMD_LOAD0,          0x0000);
				send_command (CMD_SET_SRAM_ADDR,  0     );

				send_command (CMD_SRAM_READ_INC,  0     );
				send_query   (CMD_QUERY_FETCH1);
				send_query   (CMD_QUERY_FETCH0);

			}
			break;

		default:
			// bad command - so complain
			printf("BD%04X\r\n",crc_gen("BD",2));
			// and wait for a better one
			continue;
		}

		// next four characters are either CRC or load data

		if ((command == CMD_LOAD0) || (command == CMD_LOAD1))
			{

			data = hex4char(input+2);

			// get remaining data from COM port

			while( (input[6]=nr_uart_rxchar(na_uart)) == NO_CHAR );
			while( (input[7]=nr_uart_rxchar(na_uart)) == NO_CHAR );
			while( (input[8]=nr_uart_rxchar(na_uart)) == NO_CHAR );
			while( (input[9]=nr_uart_rxchar(na_uart)) == NO_CHAR );
	
			crc_sent = hex4char(input+6);

			if( crc_gen(input,6) != crc_sent )
				nval=ERROR_BAD_CHECKSUM;
			}
		else
			{
			crc_sent = hex4char(input+2);

			if( crc_gen(input,2) != crc_sent )
				nval = ERROR_BAD_CHECKSUM;
			}

		if( nval == ERROR_BAD_CHECKSUM ) // the checksum didn't work
            {
			printf("BC%04X\r\n",crc_gen("BC",2));
			continue;
            }
		
		//na_seven_seg_pio->np_piodata=~nval;					
		        		        
        if( command == CMD_QUERY_FETCH0 ||
            command == CMD_QUERY_FETCH1 ||
			command == CMD_QUERY_INTERNAL_GET_STATUS )
            {
            unsigned int result;
            
            result=send_query( command );

			// add on CRC
			sprintf(reply,"OK%04X",result);
			sprintf(rbuf,"%04X",crc_gen(reply,6));
			// end CRC add on
			            
			printf("%s%s\r\n", reply, rbuf);
            }
        else
            {
            send_command( command , data );

			// run will not start until the command is removed from the PIO

			if (command == CMD_RUN)
			    send_command( CMD_CLEAR , 0 );           

			printf("OK%04X\r\n",crc_gen("OK",2));
            }
        }
    }

//************************************************************************************************* //
// ********************************** Other Functions ********************************************* //
// ************************************************************************************************ //

void send_command(int command,int param)
    {
	// bring command register low
	na_pio_cmd->np_piodirection = PIO_OUTPUT;
    na_pio_cmd->np_piodata = CMD_CLEAR;

	// set new data    
    na_pio_data->np_piodirection = PIO_OUTPUT;
    na_pio_data->np_piodata = param;

	// put command code into command register
	na_pio_cmd->np_piodirection = PIO_OUTPUT;
    na_pio_cmd->np_piodata = command;
    }

int send_query(int command)
    {
	if( command == CMD_QUERY_INTERNAL_GET_STATUS )
		{
		na_pio_status->np_piodirection = PIO_INPUT;
		return na_pio_status->np_piodata;
		}

	// bring command register low
	na_pio_cmd->np_piodirection = PIO_OUTPUT;
    na_pio_cmd->np_piodata = CMD_CLEAR;

		{
		// mess around pausing for a while
		int i,j;
		i=0;
		for(j=0;j<10;j++)
			i++;
		}

    na_pio_cmd->np_piodirection = PIO_OUTPUT;
    na_pio_cmd->np_piodata = command;

		{
		// mess around pausing for a while
		int i,j;
		i=0;
		for(j=0;j<10;j++)
			i++;
		}

	// fetch result back
    na_pio_data->np_piodirection = PIO_INPUT;
    return na_pio_data->np_piodata;
    }

//---------------- convert four hexadecimal characters to a 16 bit value---------------------------

UINT16 hex4char(char * inhex)
        {
		UINT16 value = 0;
		int i;
        for(i=0;i<2;i++)
            {
			UCHAR v1 = hex1char(inhex[i*2 + 0]);
			UCHAR v2 = hex1char(inhex[i*2 + 1]);			
			value = (value<<8) | (v1<<4) | v2;
			//printf("v1=%d v2=%d value=%d\r\n",v1,v2,value);
            }

		return value;
        }

//---------------- convert one hexadecimal character to a 4 bit value------------------------------

UCHAR hex1char(char c)
	{
    if( c >= '0' && c <= '9' )
		return c - '0';
    else if( c >= 'A' && c <= 'F' )
         return c - 'A' + 10;
    else if( c >= 'a' && c <= 'f' )
         return c - 'a' +10;
	else
		{
         //printf("bad hex (illegal character)\n");
         return 0;
		}
	}


//------------------------------------ crc_gen ----------------------------------

UINT16 crc_gen(const unsigned char *data, const UINT16 len)
{
	UINT16  crc;
	UINT16  i, j;

	crc = CRCINIT;
	for (i=0; i<len; i++)
	{
		crc = crc ^ data[i];
		for (j=0; j<8; j++)
		{
			if ((crc & 0x0001) > 0)
			{
				crc = crc >> 1;
				crc = crc ^ GENPOLY;
			}
			else
				crc = crc >> 1;
		}
	}

	return crc;
}

// end of desmim.c
