#include "functions.h"

#include "sdma_script_code_ROMv2.h"
#include <stdio.h>

// ---------------------------------------------
// SDMA test re-written to transfer data from
// SDRAM-to-SDRAM using channel 10 of the SDMA.
// ---------------------------------------------

// -----

/*
   0x1fff_c600 - 0x1fff_c6ff  - .bss section (*.v = load_vram())
   0x1fff_c700 - 0x1fff_ceff  - (0x800) sdma_rom_code as "expected result" (*.v = download , *.c = compare) - In Ext mem 0x80002000
   0x1fff_cf00 - 0x1fff_d6ff  - (0x800) sdma_rom_code as "actual   result" (*_s.as = copy script , *.c = compare )  - In Ext mem 0x80011000
   0x1fff_d700 - ...          - channel_10 script (*.v = download , *.c = chan_0 data structure & PRGM_CH10_PTR)

   SDMA_ROM_SIZE  0x397[word] - Attention is needed for (*_s.as = copy script , *.c = compare) in case the sdma_rom_size is odd. !!!! 

  LOCAL_SCRIPT_SIZE - channel 10 script size (*.c = chan_10 data structure) 
 */   


#define SRC			0x82000000	// source in SDRAM	
#define DEST		0x82200000	// destination in SDRAM
#define BUFF_SIZE	0x00001000	// buffer size

#define BD_DONE  0x010000
#define BD_WRAP  0x020000
#define BD_CONT  0x040000
#define BD_INTR  0x080000
#define BD_RROR  0x100000
#define BD_LAST  0x200000
#define BD_EXTD  0x800000

// ***** VARIABLES DECLARATIONS ***** //
typedef struct dummyCCB {
  unsigned long baseBDptr;
  unsigned long currentBDptr;
  unsigned long status;
  unsigned long channelDescriptor;
} channelControlBlock;


volatile unsigned int gFailCount;
channelControlBlock CCB[32];
unsigned long CTXT_CH10_PTR[32];

unsigned long BDCh0[3];
unsigned long BDCh10[3];

int sdma_interrupt_occured;
WORD sdma_interrupt;
WORD done_chan_10;
// -----


// ***** FUNCTIONS ***** //
// ----------------
// sdma_int_handler
// ----------------
// Description:
// On SDMA interrupt, sdma_int_handler is executed, SDMA
// Interrupt register is read, if it deals with Channel0 , it
// means boot is over, so channel 10 must be start. If it deals
// with channel 10, check test must be executed.
void sdma_int_handler (void)
{
//  int num_of_res = 1;
//  unsigned long expected_array[num_of_res]; 
//  unsigned long res_array[num_of_res];

  sdma_interrupt_occured = 1;
  
  sdma_interrupt = mem32_read(SDMA_INTR);
  // Clean up SDMA interrupt reg
  mem32_write(SDMA_INTR, sdma_interrupt);
  if (sdma_interrupt == 0x1){
    // ARM starts channel 10
    // <6> Start channel 10
    reg32_write(SDMA_START,0x00000400);
  }
  else {
   done_chan_10 = 1;
     // <7> Check test
//    res_array[0]      = reg32_read(reg32_read(SDMA_COPTR));
//    expected_array[0] = 0x7dfffffc;    	

//    info_trigger(0x100,res_array,res_array[0]);

//    CheckTest(expected_array,res_array,num_of_res); 	
//    verilog_trigger(arm_vt_finish); 
  }
}
/*
WORD le(WORD n){
  HALF *halfs;
  WORD *res;

  halfs = &n;

  return halfs[1]*0x10000+halfs[0];
}
*/
// -----------
// config_avic
// -----------
// Description:
// Configuration of the ARM interrupt controller
void config_avic(void)
{
  // Enable SDMA interrupt 0x34 in AVIC Interrupt Enable Number Reg
  mem32_write(AVIC_INTENNUM,0x00000034);

  // Set priority in AVIC Interrupt Enable Priority reg: 15
  mem32_write(AVIC_NIPRIORITY4,0x00000F00);

  // Register address of ISR in vector table
  CAPTURE_INTERRUPT(SDMA_INT_ROUTINE, sdma_int_handler);

}

// -----


void config_cs0(){
  // Init memory
  reg32_write(WEIM_CS0L,0x00120501);
  reg32_write(WEIM_CS0A,0x12000000);

  reg32_write(WEIM_BASE_ADDR+0x60, reg32_read(WEIM_BASE_ADDR+0x60) | 0x4); 
}

// ***** MAIN FUNCTION ***** //
void main (){

  WORD sdma_data_temp;
  int i;

//  config_cs0();

	// fill the SDRAM source
	for (i = 0; i <= BUFF_SIZE/4; i++)
	{
		*(unsigned int *)(SRC+i*4) = 0x11111111*i;
	}


// ------------------
// <1> CCB / BD setup
// ------------------

  // **** CHANNEL CONTROL BLOCK *** //
  // connect buffer descriptors with channel control block
  CCB[0].baseBDptr         = (unsigned long)&BDCh0;
  CCB[0].currentBDptr      = 0x00000000;
  CCB[0].status            = 0x00000000;
  CCB[0].channelDescriptor = 0x00000000;
  
  CCB[10].baseBDptr         = (unsigned long)&BDCh10;
  CCB[10].currentBDptr      = 0x00000000;
  CCB[10].status            = 0x00000000;
  CCB[10].channelDescriptor = 0x00000000;
  


  // **** BUFFER DESCRIPTOR *** //
  // setup buffer descriptors for channel 0 and 5
  BDCh0[0] = 0x01810020;  //SET DM - Extended - INT - CONT - DONE
  
  // buffer address 
  BDCh0[1] = (unsigned long)&CTXT_CH10_PTR;  // pointer to context of channel 10, see channel 10 context
  
  // extended buffer address
  BDCh0[2] = 0x00000940;			// where in SDMA we want to put context
  // size of each context is 32-words.  Starting adddress of RAM in SDMA
  // is 0x800 (which is start of channel 0 context). So chan10 context is loaded
  // at offset of 10 x 32-words from 0x800, or 0x940.
  
  // used to upload a script, not needed for this example since script in ROM
  //BDCh0[3] = 0x04890000 | LOCAL_SCRIPT_SIZE;  //SET PM - Extended - INT  - DONE - 16 instructions
  //BDCh0[4] = &PRGM_CH10_PTR;
  //BDCh0[5] = 0x00001800;

  //Channel 10 Context
  // first place of the context. Context[0] is the PC
  CTXT_CH10_PTR[0] = ap_2_ap_ADDR;  	// program counter, address offset in SDMA ROM
 									  	// which points to start of script, refer to SDMA 
 									  	// script header file. Address of script is 423.
  // Initialize the other context registers to zero	 									 	 								
  for (i=1;i<=31;i++){
    CTXT_CH10_PTR[i] = 0x0;  
  }
  
  // from definition of the script, it requires that this particular general purpose
  // register to be loaded with a location of the start of external memory
  // Depending on the address of the source/destination, the SDMA unit will
  // decide which internal DMA unit (perihperal or burst) to use on the transfer
  // On MX31, the start of the external memory is 0x80000000
 	CTXT_CH10_PTR[9] = 0x80000000;
  
  // update ch10 context, set the bits in the parameters for the buffer descriptor
  BDCh10[0] = 0x00810000 | BUFF_SIZE;  //SET DM - Extended - INT - CONT - DONE (need to 
  									   //validate this descriptor 
  									   // BUFF_SIZE is the count, which is number bytes
  									   // to transfer (bytes total)
  									   // burst size not set, b/c for peripheral
  									   // this is defined by the water mark for the periph
  									   // FIFO
  // set up the buffer and extended buffer descriptor  									   
  BDCh10[1] = SRC;	
  BDCh10[2] = DEST;
  
// ------------------
// <2> Pgm priority
// ------------------

//**** SDMA has a set of registers to program, which is what we are doing below
//**** refer to SDMA documentation for details of these registers

  // SDMA channel 0 pointer register (update the MC0PTR to point to CCB0)
  // need to update this pointer register  b/c CCB0 is in external memory
  reg32_write(SDMA_COPTR,(unsigned long)&CCB);

  // these set priority registers, where priority 7 is highest priority and 0 is lowest
  // setting to 0, the channel will not start, so really priority one is lowest
  // Setting to 0 is like disabling the channel, so set unused channels to 0
  // multiple channels can have same priority, second level priority is the 
  // channel number (lower channel has higher priority)	
  // CHNPRI_0: channel 0 is of pty 7
  reg32_write(SDMA_CHNPRI_0,0x00000007);
  // CHNPRI_1: channel 10 is of pty 1
  reg32_write(SDMA_CHNPRI_10,0x00000001);
  
  // Event override register.  Since SDMA not started by peripheral signal
  //  EO for channel 0/10
  reg32_write(SDMA_EVTOVR,0x00000401);
  
  
// ------------------
// <3> Pgm scratch RAM size & static context switch
// ------------------

  // set up the CHNOADDR, channel 0 boot address
  // sets up so that the context is 32 words not 24.
  			
  // Set bit for Scratch RAM
  sdma_data_temp = reg32_read(SDMA_CHN0ADDR);
  sdma_data_temp = sdma_data_temp | 0x00004000;
  reg32_write(SDMA_CHN0ADDR,sdma_data_temp);
  
// ------------------
// <4> Configure interrupt handling
// ------------------
  // Setup for AVIC and interrupt handler
//  config_avic();


  // initializing the done_chan_10 flag, would be used if interrupt handler is used
  done_chan_10 = 0;
// ------------------
// <5> Start channel 0
// ------------------
  
  // first use channel 0 to upload the context and buffer descriptor for channel 10 
  // to configure for channel 10 transfer
  
  // write to HSTART register to start channel 0

  //  HE for channel 0
  reg32_write(SDMA_START,0x00000001);
  
// polling on the done bit in the buffer descriptor parameter
// normally use the interrupt to indicate end of transfer   
//  while(done_chan_10 == 0){};
	// wait for chan 0 to end
	//for (i=0; i<=100; i++);
	while(BDCh0[0]&BD_DONE);
	
// now that transfer is done, check to see if error
// check to see if error bit is set
	if ( BDCh0[0]&BD_RROR)
	{
		printf("DMA error detected on channel 0\n");
	}
	
	printf("BDCh0[0] = 0x%x\n", BDCh0[0] );
	
// end of channel 0 trasnfer	
	
// start of channel 10 transfer	
	printf("Before Ch 10 start BDCh10[0] = 0x%x\n", BDCh10[0] );
	
	// write to HSTART register to start channel 10
	//  HE for channel 10
	reg32_write(SDMA_START,0x00000400);

    // wait till done (poll done bit)		
	while(BDCh10[0]&BD_DONE);
	
	
	// check for errors that may have occurred during transfer
	if ( BDCh10[0]&BD_RROR)
	{
		printf("DMA error detected on channel 10\n");
	}
	
	printf("After Ch 10 end BDCh10[0] = 0x%x\n", BDCh10[0] );	

	// check the SDRAM destination to ensure transfer took place
	for (i = 0; i < BUFF_SIZE/4; i++)
	{
	
		if(*(unsigned int *)(DEST+i*4) != 0x11111111*i)
		{
			gFailCount++;
			printf("SDRAM check test failed, address 0x%x\n", (DEST+i*4));
		}
		
	}
	
	if (gFailCount == 0)
	{
		printf("test passed \n");
	}
    
}
