/*
 * File: 
 * Beginning of DMA weird architecture machine
 * ---------
 * This machine makes
 * bit outputs on RA0 and RB0
 * from one of three of blocks 
 * ---------
 * The machine increments a variable
 * and uses the variable value to conditionally branch
 * between three different pulse generator DMA blocks
 * ---------
 * Bruce Land
 * Target PIC:  PIC32MX250F128B
 */
// all peripheral library includes
#include <plib.h>
////////////////////////////////////
// graphics libraries for dbugging
// SPI channel 1 connections to TFT
//#include "tft_master.h"
//#include "tft_gfx.h"

// print a line on the TFT
// string buffer
/*
char print_buffer[80];
void tft_printLine(int line_number, int indent, char* print_buffer, short text_color, short back_color, short char_size){
    // print_buffer is the string to print
    int v_pos, h_pos;
    char_size = (char_size>0)? char_size : 1 ;
    //
    v_pos = line_number * 8 * char_size ;
    h_pos = indent * 6 * char_size ;
    // erase the pixels
    //tft_fillRoundRect(0, v_pos, 239, 8, 1, back_color);// x,y,w,h,radius,color
    tft_setTextColor2(text_color, back_color); 
    tft_setCursor(h_pos, v_pos);
    tft_setTextSize(char_size);
    tft_writeString(print_buffer);
}
*/
/////////////////////////////////////////////////////////
#pragma config FNOSC = FRCPLL, POSCMOD = OFF
//#pragma config FNOSC = PRIPLL, POSCMOD = EC, OSCIOFNC = OFF
#pragma config FPLLIDIV = DIV_2, FPLLMUL = MUL_20, FPLLODIV = DIV_2  //40 MHz
#pragma config FPBDIV = DIV_1 // PB 40 MHz
// turn off alternative functions for port pins B.4 and B.5
#pragma config JTAGEN = OFF, DEBUG = OFF
#pragma config FSOSCEN = OFF

// offsets into the DMA control blcok
#define DCH0SSA_OFFSET 0x30 // DMA source address field

// DMA block image list
// The number of blocks in our execute list
// The machine will cycle through these blocks, then start again
#define number_of_blocks 8 // 10 for full pgm
// From http://people.ece.cornell.edu/land/courses/ece4760/PIC32/Microchip_stuff/2xx_datasheet.pdf
// page 52, TABLE 4-12:DMA CHANNELS 0-3 REGISTER MAP
// Total DMA control block size in bytes:
#define length_of_block 192
// the main DMA block program list
unsigned char DMA_blocks[number_of_blocks * length_of_block];
// counter for the current block to create
int N = 0;

// DMA block jump array alternatives
// One of these blocks will be swapped into the execution block list
// on-the-fly by a conditional jump sequence
#define number_of_jump_blocks 3
unsigned char DMA_jump_blocks[number_of_jump_blocks * length_of_block];
// counter for current jump block to create
int N_jump = 0;

// DMA output tables to LEDs
unsigned char LED_pattern0[] =
{
	0x01,	0x00,	0x01,	0x00,	0x01,	0x00,	0x01,	0x00,
	0x01,	0x00,	0x01,	0x00,	0x01,	0x00,	0x01,	0x00
} ;
// DMA output tables
unsigned char	LED_pattern1[]=
{
	0x01,	0x01,	0x01,	0x01,	0x01,	0x01,	0x01,	0x01,
	0x00,	0x00,	0x00,	0x00,	0x00,	0x00,	0x00,	0x00,
    0x01,	0x01,	0x01,	0x01,	0x01,	0x01,	0x01,	0x01,
	0x00,	0x00,	0x00,	0x00,	0x00,	0x00,	0x00,	0x00
};

// DMA output tables 64 bytes
unsigned char	LED_pattern2[]=
{
	0x01,	0x01,	0x01,	0x01,	0x01,	0x01,	0x01,	0x01,
    0x01,	0x01,	0x01,	0x01,	0x01,	0x01,	0x01,	0x01,
	0x00,	0x00,	0x00,	0x00,	0x00,	0x00,	0x00,	0x00,
    0x00,	0x00,	0x00,	0x00,	0x00,	0x00,	0x00,	0x00,
    0x01,	0x01,	0x01,	0x01,	0x01,	0x01,	0x01,	0x01,
    0x01,	0x01,	0x01,	0x01,	0x01,	0x01,	0x01,	0x01,
	0x00,	0x00,	0x00,	0x00,	0x00,	0x00,	0x00,	0x00,
    0x00,	0x00,	0x00,	0x00,	0x00,	0x00,	0x00,	0x00,
};

// DMA table for incrementing a variable (just a few entries for now) 
// [index0]=1 [index1]=2 [index2] wraps back to zero
// it is aligned so that the value in the array can be used as the low-byte
// of the pointer to the next element
unsigned char inc_array[] __attribute__ ((aligned(256))) = {1, 2, 0} ;
unsigned char inc_value=1 ;

// array to convert 1 byte inc to four byte jump table pointer
// This is just inc_value*4, but all arithmetic is a table-lookup
// The array is aligned on a byte boundary so that the one-byte index
// can just be copied into the source field of the next DMA block
unsigned char offset_array[] __attribute__ ((aligned(256))) = {4, 8, 0} ;

// DMA table for computing a branch 
// [index0]=address of target block if variable=0
// [index1]=address of target block if variable=1
// it is aligned so that the value in the offset_array can be used as the low-byte
// of the pointer to the next element
unsigned int jmp_array[3] __attribute__ ((aligned(256))) ;

// to clear bits in DMA 0 flag register
// using DMA 1-3 transfer
unsigned short clear_flag = 0x0000 ;

// === Main  ======================================================
void main(void) {
 
    SYSTEMConfig(40000000, SYS_CFG_WAIT_STATES | SYS_CFG_PCACHE);
  
    // Build the actual address map for moving the jump target block
    // into the execution path
    // NOTE that these VIRTUAL addresses MUST be converted to physical addresses
    // On this architecture, the physical address of RAM is just the two lower bytes
    // of the virtual address
    jmp_array[0] = DMA_jump_blocks ;
    jmp_array[1] = DMA_jump_blocks + length_of_block ;
    jmp_array[2] = DMA_jump_blocks + length_of_block*2 ;
    
    //==0==========
    // In the following code, the DMA definition functions are used to build
    // DMA blocks, which are then copied to memory sequentially.
    // They will be executed later when loaded into an active DMA channel
    // using a DMA fetch/execute machine
    //=============
    //Define DMA block  and store it
    // Trigger pulse for scope
    // One approx 0.1 microSec pulse on A0
    DmaChnOpen(2, 0, DMA_OPEN_AUTO);
    DmaChnSetTxfer(2, LED_pattern0, (void*)&LATA, 2, 1, 2);
    DmaChnSetEventControl(2, DMA_EV_START_IRQ(_DMA0_IRQ));
    DmaChnSetEvEnableFlags(2, DMA_EV_CELL_DONE);
    DmaChnEnable(2);
    // copy to array
    memcpy(DMA_blocks+length_of_block*N, &DCH2CON, length_of_block);
    N++;
    
    /* comment out two blocks for testing
    //==1==========
    //Define DMA block  and store it in block table
    // One approx 2 microSec pulse on B0
    DmaChnOpen(2, 0, DMA_OPEN_AUTO);
    DmaChnSetTxfer(2, LED_pattern2, (void*)&LATB, 32, 1, 32);
    DmaChnSetEventControl(2, DMA_EV_START_IRQ(_DMA0_IRQ));
    DmaChnSetEvEnableFlags(2, DMA_EV_CELL_DONE);
    DmaChnEnable(2);
    // now move to array
    memcpy(DMA_blocks+length_of_block*N, &DCH2CON, length_of_block);
    N++;
    
    //==2==========
    //Define DMA block  and store it
    // Two approx 2 microSec pulses on B0
    DmaChnOpen(2, 0, DMA_OPEN_AUTO);
    DmaChnSetTxfer(2, LED_pattern2, (void*)&LATB, 64, 1, 64);
    DmaChnSetEventControl(2, DMA_EV_START_IRQ(_DMA0_IRQ));
    DmaChnSetEvEnableFlags(2, DMA_EV_CELL_DONE);
    DmaChnEnable(2);
    // base address + 
    memcpy(DMA_blocks+length_of_block*N, &DCH2CON, length_of_block);
    N++;
    */
    
    //==3==========
    // Increment a variable--
    // part 1 -- read variable, write to low-byte of source in next block
    // part 2 -- in the next block, read array element, write to variable
    // note that the CONTENTs of the array is the incremented value of the INDEX
    //Define DMA block  and store it
    // increment a variable -- part 1 
    // -- read the variable into the next block source reg
    DmaChnOpen(2, 0, DMA_OPEN_AUTO);
    DmaChnSetTxfer(2, &inc_value, (void*)(DMA_blocks+length_of_block*(N+1)+DCH0SSA_OFFSET), 1, 1, 1);
    DmaChnSetEventControl(2, DMA_EV_START_IRQ(_DMA0_IRQ));
    DmaChnSetEvEnableFlags(2, DMA_EV_CELL_DONE);
    DmaChnEnable(2);
    // base address + 
    memcpy(DMA_blocks+length_of_block*N, &DCH2CON, length_of_block);
    N++;
    
    //==4==========
    //Define DMA block  and store it
    // increment a variable -- part 2 
    // -- read array entry into variable
    DmaChnOpen(2, 0, DMA_OPEN_AUTO);
    // !!!the inc_array ptr (low byte)!!! 
    ///!!!WILL BE MODIFIED!!! by the previous block 
    // to specify the array index 
    DmaChnSetTxfer(2, inc_array, &inc_value, 1, 1, 1);
    DmaChnSetEventControl(2, DMA_EV_START_IRQ(_DMA0_IRQ));
    DmaChnSetEvEnableFlags(2, DMA_EV_CELL_DONE);
    DmaChnEnable(2);
    // base address + 
    memcpy(DMA_blocks+length_of_block*N, &DCH2CON, length_of_block);
    N++ ;
    
    //==5==========
    // Compute a branch --
    // part 1 -- read variable, write to low-byte of source in next block
    // part 2 -- read offset array element, to get offset into jump array
    // part 3 -- read the jump array to get beginning of target block
    // part 4 -- read the target block to move the target block into line for execution
    //Define DMA block  and store it
    // branch -- part 1 
    // -- read array entry into variable
    DmaChnOpen(2, 0, DMA_OPEN_AUTO);
    DmaChnSetTxfer(2, &inc_value, (void*)(DMA_blocks+length_of_block*(N+1)+DCH0SSA_OFFSET), 1, 1, 1);
    DmaChnSetEventControl(2, DMA_EV_START_IRQ(_DMA0_IRQ));
    DmaChnSetEvEnableFlags(2, DMA_EV_CELL_DONE);
    DmaChnEnable(2);
    // base address + 
    memcpy(DMA_blocks+length_of_block*N, &DCH2CON, length_of_block);
    N++;
    
    //==6==========
    // Compute a branch --
    // part 2 -- in the next block, read array element, write to cell-source of DMA0
    // note that the CONTENTs of the array is the address-of-next-block-to execute 
    //Define DMA block  and store it
    // the address "offset-array" is modified by the pervious block
    DmaChnOpen(2, 0, DMA_OPEN_AUTO);
    DmaChnSetTxfer(2, offset_array, (void*)(DMA_blocks+length_of_block*(N+1)+DCH0SSA_OFFSET), 1, 1, 1);
    DmaChnSetEventControl(2, DMA_EV_START_IRQ(_DMA0_IRQ));
    DmaChnSetEvEnableFlags(2, DMA_EV_CELL_DONE);
    DmaChnEnable(2);
    // base address + 
    memcpy(DMA_blocks+length_of_block*N, &DCH2CON, length_of_block);
    N++;
    
    //==7==========
    //Define DMA block  and store it
    // branch -- part 3 
    // -- read array entry into DMA0 cell-source field
    DmaChnOpen(2, 0, DMA_OPEN_AUTO);
    // !!!the offset_array ptr (low byte)!!! 
    ///!!!WILL BE MODIFIED!!! by the previous block 
    // to specify the array index 
    // the address "jmp-array" is MODIFIED by the pervious block
    // ONLY two bytes are tranfered to the next block because memory is only )x1fff in size
    DmaChnSetTxfer(2, jmp_array, (void*)(DMA_blocks+length_of_block*(N+1)+DCH0SSA_OFFSET), 2, 2, 2);
    DmaChnSetEventControl(2, DMA_EV_START_IRQ(_DMA0_IRQ));
    DmaChnSetEvEnableFlags(2, DMA_EV_CELL_DONE);
    DmaChnEnable(2);
    // base address + 
    memcpy(DMA_blocks+length_of_block*N, &DCH2CON, length_of_block);
    N++ ;
    
    //==8==========
    //Define DMA block  and store it
    // branch -- part 4 
    // -- read array entry into DMA0 cell-source field
    DmaChnOpen(2, 0, DMA_OPEN_AUTO);
    // !!!the offset_array ptr (low byte)!!! 
    ///!!!WILL BE MODIFIED!!! by the previous block 
    // to specify the array index 
    // the address "DMA_jump_blocks" is REPLACED by the transfer from the previous block
    DmaChnSetTxfer(2, DMA_jump_blocks, (void*)(DMA_blocks+length_of_block*(N+1)), length_of_block, length_of_block, length_of_block);
    DmaChnSetEventControl(2, DMA_EV_START_IRQ(_DMA0_IRQ));
    DmaChnSetEvEnableFlags(2, DMA_EV_CELL_DONE);
    DmaChnEnable(2);
    // base address + 
    memcpy(DMA_blocks+length_of_block*N, &DCH2CON, length_of_block);
    N++ ;
    
    //==9==========
    //Define DMA block  and store it
    // -- branch target block -- 
    // null block! will be replaced on-the-fly
    // by the block loader just above
    //N++ ;
    
    //=============
    // A SEPARATE list of jump blocks to be move to block position 8 above
    //Define DMA block  and store it in junp block table
    // One approx 1 microSec pulse on B0
    DmaChnOpen(2, 0, DMA_OPEN_AUTO);
    DmaChnSetTxfer(2, LED_pattern2, (void*)&LATB, 32, 1, 32);
    DmaChnSetEventControl(2, DMA_EV_START_IRQ(_DMA0_IRQ));
    DmaChnSetEvEnableFlags(2, DMA_EV_CELL_DONE);
    DmaChnEnable(2);
    // now move to array
    memcpy(DMA_jump_blocks+length_of_block*N_jump, &DCH2CON, length_of_block);
    N_jump++;
    
    //Define DMA block  and store it in junp block table
    // One approx 1 microSec pulse on B0
    DmaChnOpen(2, 0, DMA_OPEN_AUTO);
    DmaChnSetTxfer(2, LED_pattern2, (void*)&LATB, 64, 1, 64);
    DmaChnSetEventControl(2, DMA_EV_START_IRQ(_DMA0_IRQ));
    DmaChnSetEvEnableFlags(2, DMA_EV_CELL_DONE);
    DmaChnEnable(2);
    // now move to array
    memcpy(DMA_jump_blocks+length_of_block*N_jump, &DCH2CON, length_of_block);
    N_jump++;
    
    //Define DMA block  and store it in junp block table
    // One approx 1 microSec pulse on B0
    DmaChnOpen(2, 0, DMA_OPEN_AUTO);
    DmaChnSetTxfer(2, LED_pattern1, (void*)&LATB, 16, 1, 16);
    DmaChnSetEventControl(2, DMA_EV_START_IRQ(_DMA0_IRQ));
    DmaChnSetEvEnableFlags(2, DMA_EV_CELL_DONE);
    DmaChnEnable(2);
    // now move to array
    memcpy(DMA_jump_blocks+length_of_block*N_jump, &DCH2CON, length_of_block);
    N_jump++;
    
    //=============
    // now kill DMA2 channel so that
    // it can be started later by DMA0 block 0
    DmaChnDisable(2);
    DmaChnClrEvEnableFlags(2, DMA_EV_ALL_EVNTS) ;
    
    // === DMA fetch/execute setup  =====================
    // Open the all the  DMA channels, 
    // except DMA2 which will be loaded and triggered by DMA0 
	// Enable the AUTO option for all
	DmaChnOpen(0, 0, DMA_OPEN_AUTO);
    DmaChnOpen(1, 0, DMA_OPEN_AUTO);
    //DmaChnOpen(2, 0, DMA_OPEN_AUTO);
    DmaChnOpen(3, 0, DMA_OPEN_AUTO);
    
	// set the transfer parameters: source & destination address,
    //   source & destination size, number of bytes per event
    // DMA0 writes DMA control blocks from an array (DMA program) to DMA2
    // DMA1 writes to the DMA0 control block to clear a flag
    // DMA2 moves data as specified in the block array defined earlier
    // DMA3 writes to the DMA2 control block to clear a flag  
    // number_of_blocks
    DmaChnSetTxfer(0, DMA_blocks, (void*)&DCH2CON, number_of_blocks*length_of_block, length_of_block, length_of_block);
    DmaChnSetTxfer(1, &clear_flag, (void*)&DCH0INT, 2, 2, 2);
    //DmaChnSetTxfer(2, LED_pattern1, (void*)&LATB, 16, 1, 1);
    DmaChnSetTxfer(3, &clear_flag, (void*)&DCH2INT, 2, 2, 2);

	// set the transfer event control: what event is to start the DMA transfers
        // In this case, DMA0 triggers DMA2 and vice-versa
        // and cell transfer done on DMA0 triggers channel 1 to clear the DMA0 flag
        // and cell transfer done on DMA2 triggers channel 3 to clear the DMA2 flag
    // net result is to bounce between DMA0 and DMA2 as fast as possible
    // BUT you need to force a transfer to start the system
	DmaChnSetEventControl(0, DMA_EV_START_IRQ(_DMA2_IRQ)); //_TIMER_2_IRQ
    DmaChnSetEventControl(1, DMA_EV_START_IRQ(_DMA0_IRQ)); 
    //DmaChnSetEventControl(2, DMA_EV_START_IRQ(_DMA0_IRQ)); 
    DmaChnSetEventControl(3, DMA_EV_START_IRQ(_DMA2_IRQ));
	
    // Set up the cell transfer done flags
    // needed to set up cell done enable bit
    DmaChnSetEvEnableFlags(0, DMA_EV_CELL_DONE);
    //DmaChnSetEvEnableFlags(2, DMA_EV_CELL_DONE);
    
    // start the channels
    DmaChnEnable(0);
    DmaChnEnable(1);
    //DmaChnEnable(2);
    DmaChnEnable(3);
    
    // force the first transfer for DMA0.
    // After this, the system just runs
    DmaChnForceTxfer(0);
    
    // set up i/o port pins
    ANSELA =0; // turn off analog on A
    ANSELB =0; // turn off analog on B
    mPORTAClearBits(BIT_0 );		//Clear A bits to ensure light is off.
    mPORTASetPinsDigitalOut(BIT_0 );    //Set A port as output
    mPORTBClearBits(BIT_0 );		//Clear B bits to ensure light is off.
    mPORTBSetPinsDigitalOut(BIT_0 );    //Set B port as output
    
    // init the display
  // NOTE that this init assumes SPI channel 1 connections
  //tft_init_hw();
  //tft_begin();
  //tft_fillScreen(ILI9340_BLACK);
  //240x320 vertical display
  //tft_setRotation(1); // Use tft_setRotation(1) for 320x240
  OpenTimer2(T2_ON | T2_SOURCE_INT | T2_PS_1_32, 0xffff);
  unsigned int t;
  char buffer[60];
	while(1){
        // 
        t = ReadTimer2();
        if(t>32000){
            WriteTimer2(0);
            sprintf(buffer, "%x %x %x", jmp_array[0],jmp_array[1],jmp_array[2]);
            //tft_printLine(1, 0, buffer, ILI9340_WHITE, ILI9340_BLACK, 2);
        }
    };   
  } // main

// === end  ======================================================

