/************************************************************************/
/*																		*/
/*	cm1kwire.cpp	--	I2c Driver for the CM1k chip			        */
/*	based on BSB_BrainCard												*/
/************************************************************************/
/*																		
 *	BraincardNeurons.cpp	--	Driver for the Braincard			        	
 *  revision 1.1, September 16, 2016
 *  Updated November 30, 2016 
 *	Copyright (c) 2016, General Vision Inc, All rights reserved	
 *
 * http://general-vision.com/documentation/TM_NeuroMem_Technology_Reference_Guide.pdf
 *
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its contributors
 * may be used to endorse or promote products derived from this software without
 * specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
/******************************************************************************/

/* ------------------------------------------------------------ */
/*				Include File Definitions						*/
/* ------------------------------------------------------------ */

#include <cm1kwire.h>

using namespace std;
extern "C" {
  #include <stdint.h>
}

// ------------------------------------------------------------ //
//    Constructor to the class cm1kwire
// ------------------------------------------------------------ 
cm1kwire::cm1kwire(){	
}


// --------------------------------------------------------
// Wire Read the register of a given module (module + reg = addr)
//---------------------------------------------------------
int cm1kwire::Read(unsigned char module, unsigned char reg)
{ 
    Wire.beginTransmission(module);
    Wire.write(reg);
    Wire.endTransmission();
    Wire.requestFrom(module, 2);
        while(Wire.available() < 2){}
		
        int data = Wire.read();
        data = (data << 8) + Wire.read();
        return(data);
}
// ---------------------------------------------------------
// Wire Write the register of a given module (module + reg = addr)
// ---------------------------------------------------------
void cm1kwire::Write(unsigned char module, unsigned char reg, int data)
{
	Wire.beginTransmission(module);
	Wire.write(reg);
	Wire.write((unsigned char)(data >> 8)); // upper data
	Wire.write((unsigned char)(data & 0x00FF)); // lower data
	Wire.endTransmission();
}
// ---------------------------------------------------------
// Broadcast a vector
// not located in cognipat.ccp because is optimized for the simu (bypass of Read_Addr)
// ---------------------------------------------------------
void cm1kwire::Broadcast(unsigned char vector[], int length)
{
	// write the N components
	
	for (int i = 0; i < length-1; i++)
	{
		Write(CM1K, COMP, vector[i]);
	}


	
	Write(CM1K, LCOMP, vector[length-1]);

}
//-----------------------------------------------
// Learn a vector using the current context value
//----------------------------------------------
int cm1kwire::Learn(unsigned char vector[], int length, int category)
{
	Broadcast(vector, length);
	Write(CM1K, CAT,category);
	return(Read(CM1K,NCOUNT));
}
//----------------------------------------------
// Recognize a vector and return the best match, or the 
// category, distance and identifier of the top firing neuron
//----------------------------------------------
int cm1kwire::BestMatch(unsigned char vector[], int length, int* distance, int* category, int* nid)
{
	Broadcast(vector, length);
	*distance = Read(CM1K, DIST);
	*category= Read(CM1K, CAT); 
	*nid =Read(CM1K, NID);
	return(Read(CM1K, NSR));
}
//----------------------------------------------
// Recognize a vector and return the response  of up to K top firing neurons
// The response includes the distance, category and identifier of the neuron
// The Degenerated flag of the category is masked rmask the degenerated response, use the current context value
// Return the number of firing neurons or K whichever is smaller
//----------------------------------------------
int cm1kwire::Recognize(unsigned char vector[], int length, int K, int distance[], int category[], int nid[])
{
	int recoNbr=0;
	Broadcast(vector, length);
	for (int i=0; i<K; i++)
	{
		distance[i] = Read(CM1K, DIST);
		if (distance[i]==0xFFFF)
		{ 
			category[i]=0xFFFF;
			nid[i]=0xFFFF;
		}
		else
		{
			recoNbr++;
			category[i]= Read(CM1K, CAT);
			nid[i] =Read(CM1K, NID);
		}
	}
return(recoNbr);
}
//-------------------------------------------------------------
// Read the contents of the neuron pointed by index in the chain of neurons
// Returns an array of 264 bytes with the following format
// 2-bytes NCR, 256-bytes COMP, 2-bytes AIF, 2-bytes MINIF, 2-bytes CAT
//-------------------------------------------------------------
void cm1kwire::ReadNeuron(int index, unsigned char neuron[])
{
	int TempNSR=Read(CM1K, NSR); // save value to restore NN status upon exit of the function
	Write(CM1K, NSR, 0x0010);
	Write(CM1K, RESETCHAIN, 0);
	for (int i=0; i<index; i++) Read(CM1K, CAT); // move to index in the chain of neurons
	int Temp=Read(CM1K, NCR);
	neuron[0]=(unsigned char)((Temp & 0xFF00)>>8);
	neuron[1]=(unsigned char)(Temp & 0x00FF);
	for (int j=0; j<256; j++) { Temp=Read(CM1K, COMP); neuron[j+2]= (unsigned char)Temp;}
	Temp=Read(CM1K, AIF);
	neuron[258]=(unsigned char)((Temp & 0xFF00)>>8);
	neuron[259]=(unsigned char)(Temp & 0x00FF);
	Temp=Read(CM1K, MINIF);
	neuron[260]=(unsigned char)((Temp & 0xFF00)>>8);
	neuron[261]=(unsigned char)(Temp & 0x00FF);
	Temp=Read(CM1K, CAT);
	neuron[262]=(unsigned char)((Temp & 0xFF00)>>8);
	neuron[263]=(unsigned char)(Temp & 0x00FF);
	Write(CM1K, NSR, TempNSR); // set the NN back to its calling status
}
//----------------------------------------------------------------------------
// Read the contents of "ncount" neurons, with ncount being less than or equal
// to the number of committed neurons. 
// Returns an array of ncount records of 264 bytes with the following format
// 2-bytes NCR, 256-bytes COMP, 2-bytes AIF, 2-bytes MINIF, 2-bytes CAT
//----------------------------------------------------------------------------
void cm1kwire::ReadNeurons(unsigned char neurons[], int ncount)
{
	int TempNSR=Read(CM1K, NSR); // save value to restore NN status upon exit of the function
	Write(CM1K, NSR, 0x0010);
	Write(CM1K, RESETCHAIN, 0);
	for (int i=0; i<ncount; i++)
	{
		neurons[(i*264)+0]=0;
		int Temp=Read(CM1K, NCR);
		neurons[(i*264)+1]=(unsigned char)(Temp & 0x00FF);
		for (int j=0; j<256; j++) { Temp=Read(CM1K, COMP); neurons[(i*264)+j+2]= (unsigned char)Temp;}
		Temp=Read(CM1K, AIF);
		neurons[(i*264)+258]=(unsigned char)((Temp & 0xFF00)>>8);
		neurons[(i*264)+259]=(unsigned char)(Temp & 0x00FF);
		Temp=Read(CM1K, MINIF);
		neurons[(i*264)+260]=(unsigned char)((Temp & 0xFF00)>>8);
		neurons[(i*264)+261]=(unsigned char)(Temp & 0x00FF);
		Temp=Read(CM1K, CAT);
		neurons[(i*264)+262]=(unsigned char)((Temp & 0xFF00)>>8);
		neurons[(i*264)+263]=(unsigned char)(Temp & 0x00FF);
	}
	Write(CM1K, NSR, TempNSR); // set the NN back to its calling status
}
//---------------------------------------------------------------------
// Clear and restore the content of ncount neurons from an array of
// ncount records of 264 bytes with the following format:
// 2-bytes NCR, 256-bytes COMP, 2-bytes AIF, 2-bytes MINIF, 2-bytes CAT
//---------------------------------------------------------------------
void cm1kwire::WriteNeurons(unsigned char neurons[], int ncount)
{
	int TempNSR=Read(CM1K, NSR); // save value to restore NN status upon exit of the function
	int TempGCR=Read(CM1K, GCR);
	Write(CM1K, FORGET, 0);
	Write(CM1K, NSR, 0x0010);
	Write(CM1K, RESETCHAIN,0 );
	for (int i=0; i<ncount; i++)
	{
		Write(CM1K, NCR, neurons[(i*264)+1]);
		for (int j=0; j<256; j++) Write(CM1K, COMP, neurons[(i*264)+j+2]);
		Write(CM1K, AIF, (neurons[(i*264)+258]<<8)+neurons[(i*264)+259]);
		Write(CM1K, MINIF, (neurons[(i*264)+260]<<8)+neurons[(i*264)+261]);
		Write(CM1K, CAT, (neurons[(i*264)+262]<<8)+neurons[(i*264)+263]);
	}
	Write(CM1K, NSR, TempNSR); // set the NN back to its calling status
	Write(CM1K, GCR, TempGCR);
}
