#include "MemoryFunctions.h"


#define START_ADR_FLASH					0x100000

void write_xmemory(unsigned long addr, unsigned int data);
unsigned int flash_read_word(unsigned long adr);
void flash_reset();

unsigned char flash_toggle_bit(long adr)
{
  unsigned int dq1,dq2,tog,tim;
  unsigned long cicle;

	cicle=0;
   	dq1 = flash_read_word(adr);
	do
	{
	    dq2= flash_read_word(adr);
	    tog = (dq1 & 0x40) + ( dq2 & 0x40);
	    if (tog!=0x40)    
	       return 0;
	    dq1=dq2;
	    tim=dq2 & 0x20;
		cicle++;
	} while ((cicle!=26553500) && (tim==0));
    dq1 = flash_read_word(adr);
    dq2 = flash_read_word(adr);
    tog = (dq1 & 0x40) + ( dq2 & 0x40);
    if (tog!=0x40)    
       return 0;
       
    flash_reset();   
	return 1;
	
}

#pragma CODE_SECTION(ReadMemory,".fast_run");
unsigned int ReadMemory(unsigned long addr)
{
   return (*(volatile int *)(addr));
}

#pragma CODE_SECTION(WriteMemory,".fast_run");
void WriteMemory(unsigned long addr, unsigned int data)
{
   (*(volatile int *)( addr )) = data;
}

unsigned char flash_erase_sector(unsigned long adr)
{
	write_xmemory(0x555,0xaa);
    write_xmemory(0x2aa,0x55);
    write_xmemory(0x555,0x80);
    write_xmemory(0x555,0xaa);
    write_xmemory(0x2aa,0x55);
    write_xmemory(adr,0x30);
    
    return flash_toggle_bit(adr);
}

#pragma CODE_SECTION(write_xmemory,".fast_run");
void write_xmemory(unsigned long addr, unsigned int data)
{
   (*(volatile int *)(START_ADR_FLASH+addr)) = data;
}

#pragma CODE_SECTION(read_xmemory,".fast_run");
unsigned int read_xmemory(unsigned long addr)
{
   return (*(volatile int *)(START_ADR_FLASH+addr));
}

unsigned int flash_read_word(unsigned long adr)
{
    return read_xmemory(adr);
}

void flash_reset()
{
   write_xmemory(0,0xf0);
}

unsigned int flash_write_word(unsigned long adr, unsigned int dat)
{
  unsigned int dq=0xffff;
  
	if (dat!=dq)
	{
	    write_xmemory(0x555,0xaa );
	    write_xmemory(0x2aa,0x55 );
	    write_xmemory(0x555,0xa0 );
	    write_xmemory(adr,dat);
        dq=flash_toggle_bit(adr);
		dq=flash_read_word(adr);
	}
	return (dq==dat);
}


unsigned int RunFlashData(unsigned long AdrFrom, unsigned long AdrTo, unsigned long Length,
                          unsigned int *cerr_out, unsigned int *repl_out, unsigned int *count_ok_out)
{


	unsigned long adr_start,adr_end,f_s,f_e;
	int i;
	static char flash_tab[] = {
	32,32,32,32,32,32,32,32,32,32,32,32,32,32,16,4,4,8
	};

	unsigned int d1,d2, d3, cerr=0, repl = 0, count_ok = 0;
	unsigned long adr_out,adr_in, adr_out_s;

	 *cerr_out = 0;
	 *repl_out = 0;
	 *count_ok_out   = 0;


   
   flash_reset();

   i=0;
   adr_start=AdrTo-START_ADR_FLASH;
   adr_end=adr_start+Length;


   f_s=0;
   f_e=0;
    
   for (i=0;i<16;i++)
   {
      f_s=f_e;
	  f_e=f_s+(unsigned long)flash_tab[i]*1024;

      if ( f_s<=adr_start && f_e>adr_start   ) flash_erase_sector(f_s);
      if ( f_s>adr_start && f_e<adr_end   ) flash_erase_sector(f_s);
      if ( f_s<adr_end && f_e>adr_end   ) flash_erase_sector(f_s);
   }


//   i=flash_erase_sector(0x20000);
//   i=flash_erase_sector(0x28000);
//   i=flash_erase_sector(0x30000);
//   i=flash_erase_sector(0x38000);

      
   if (i!=0)
   {
   // error 
   //  delay_loop();
   }

   //check clear flash?
    adr_out_s=AdrTo-START_ADR_FLASH;
    cerr=0;
    adr_in=AdrFrom;
    for (adr_out = 0; adr_out < Length; adr_out++)
    {
       d1=flash_read_word(adr_out+adr_out_s);
       if (d1!=0xffff)
       {
         cerr++;
       }
    }

    if (cerr)
    {
      *cerr_out = cerr;
      *repl_out = 0;
      *count_ok_out   = 0;
      return RETURN_FLASHED_NOT_CLEAR_1;
    }
    // end check clear flash


 // пишем во flash
 adr_out_s = AdrTo-START_ADR_FLASH;
 cerr      = 0;
 adr_in    = AdrFrom;
  
  
 for (adr_out = 0; adr_out < Length; adr_out++)
 {

  d1=flash_read_word(adr_out+adr_out_s);
  d3=ReadMemory(adr_in);
  adr_in++;
  
  if (d1==0xffff) // память чистая?
  {

    flash_write_word(adr_out+adr_out_s,d3);

    d2=flash_read_word(adr_out+adr_out_s);

    if (d2!=d3)
    {
        // вторая попытка
        repl++;

        if (d2==0xffff)
        {
          flash_write_word(adr_out+adr_out_s,d3);
          d2=flash_read_word(adr_out+adr_out_s);

          if (d2!=d3)
          {
            cerr++;

            *cerr_out = cerr;
            *repl_out = repl;
            *count_ok_out   = count_ok;
            return RETURN_FLASHED_ERROR_AFTER_REPL;
          }
          else
              count_ok++;
        }
        else
        {
            // что-то записалось, но с ошибкой, надо бы опять стирать сектор
            cerr++;
            *cerr_out = cerr;
            *repl_out = repl;
            *count_ok_out   = count_ok;
            return RETURN_FLASHED_ERROR_BEFORE_REPL_NOT_CLEAR;
        }

    }
    else
        count_ok++;

  }
  else
  {
    //не стерта почему-то! ошибка!
    cerr++;
    *cerr_out = cerr;
    *repl_out = repl;
    *count_ok_out   = count_ok;
    return RETURN_FLASHED_NOT_CLEAR_2;
  }
  
 } 
 
 *cerr_out = cerr;
 *repl_out = repl;
 *count_ok_out   = count_ok;


 if (cerr)
   return RETURN_FLASHED_ERROR;

 return RETURN_FLASHED_OK;


   
   
}



unsigned int VerifyFlashData(unsigned long AdrFrom, unsigned long AdrTo, unsigned long Length,
                          unsigned int *cerr_out, unsigned int *repl_out, unsigned int *count_ok_out)
{


    unsigned long adr_start,adr_end,f_s,f_e;
    int i;

    volatile unsigned int d1,d2, d3, cerr=0, repl = 0, count_ok = 0;
    unsigned long adr_out,adr_in, adr_out_s;

     *cerr_out = 0;
     *repl_out = 0;
     *count_ok_out   = 0;

     i=0;
     adr_start=AdrTo-START_ADR_FLASH;
     adr_end=adr_start+Length;


     f_s=0;
     f_e=0;


     // test flash
     adr_out_s = AdrTo-START_ADR_FLASH;
     cerr      = 0;
     adr_in    = AdrFrom;


     for (adr_out = 0; adr_out < Length; adr_out++)
     {

      d1=flash_read_word(adr_out+adr_out_s);
      d3=ReadMemory(adr_in);
      adr_in++;

      repl++;

      if (d1!=d3)
      {
        cerr++;
      }
      else
        count_ok++;

     }


     *cerr_out = cerr;
     *repl_out = count_ok;
     *count_ok_out   = count_ok;


     if (cerr)
       return RETURN_FLASHED_ERROR;

     return RETURN_FLASHED_OK;

}



