NM500 / NeuroShield

Dependents:   NeuroShield_SimpleScript NeuroShield_andIMU NeuroShield_Gesture_Recognition

NeuroShield.cpp

Committer:
nepes_ai
Date:
2020-02-11
Revision:
3:6163399b611e
Parent:
2:2812bcbcaaea

File content as of revision 3:6163399b611e:

/*
 * NeuroShield.cpp - Driver for NeuroShield
 * Copyright (c) 2016, General Vision Inc, All rights reserved
 * Copyright (c) 2017, nepes inc, All rights reserved.
 *
 * 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.
 *
 */

/*
 * Revision History (v1.1.5)
 * 2020/02/04    v1.1.5    Add dummy for switching between sd-card and nm spi
 * 2018/06/22    v1.1.4    Minor changes
 * 2018/01/03    v1.1.3    Add burst-mode read
 * 2017/12/20    v1.1.2    Modify the structure of neurondata
 * 2017/12/11    v1.1.1    Add Powersave command and Minor changes to the library
 * 2017/08/17    v1.0.0    First Release
 */

#include "mbed.h"
#include <NeuroShield.h>
#include <NeuroShieldSPI.h>

NeuroShieldSPI spi;

// ------------------------------------------------------------ //
//    Constructor to the class NeuroShield
// ------------------------------------------------------------
NeuroShield::NeuroShield() {
}

// ------------------------------------------------------------ 
// Initialize the neural network
// 0: fail,  other: success(return total_neurons)
// ------------------------------------------------------------
uint16_t NeuroShield::begin()
{
    bool read_val = spi.connect();
    
    if (read_val != 1) {
        return(0);
    }
    else {
        countTotalNeurons();
        clearNeurons();
        
        uint16_t fpga_version = spi.version();
        if ((fpga_version != 0x0001) && (fpga_version != 0x0002))
            support_burst_read = 1;
        else
            support_burst_read = 0;
        
        return(total_neurons);
    }
}

// --------------------------------------------------------
// Get/Set the Neuron Context Register
//---------------------------------------------------------
void NeuroShield::setNcr(uint16_t value)
{
    spi.write(NM_NCR, value);
    POWERSAVE;
}

uint16_t NeuroShield::getNcr()
{
    uint16_t ret_val = spi.read(NM_NCR);
    POWERSAVE;
    return(ret_val);
}

// --------------------------------------------------------
// Get/Set the COMP register (component)
//---------------------------------------------------------
void NeuroShield::setComp(uint8_t value)
{
    spi.write(NM_COMP, (value & 0x00FF));
    POWERSAVE;
}

uint8_t NeuroShield::getComp()
{
    uint8_t ret_val = (uint8_t)(spi.read(NM_COMP) & 0x00FF);
    POWERSAVE;
    return(ret_val);
}

// --------------------------------------------------------
// Set the LCOMP register (last component)
//---------------------------------------------------------
void NeuroShield::setLastComp(uint8_t value)
{
    spi.write(NM_LCOMP, (value & 0x00FF));
    POWERSAVE;
}

// --------------------------------------------------------
// Set the Component Index register
//---------------------------------------------------------
void NeuroShield::setIndexComp(uint16_t value)
{
    spi.write(NM_INDEXCOMP, value);
    POWERSAVE;
}

// --------------------------------------------------------
// Get the Distance register
//---------------------------------------------------------
uint16_t NeuroShield::getDist()
{
    uint16_t ret_val = spi.read(NM_DIST);
    POWERSAVE;
    return(ret_val);
}

// --------------------------------------------------------
// Get/Set the Category register
//---------------------------------------------------------
void NeuroShield::setCat(uint16_t value)
{
    spi.write(NM_CAT, value);
    POWERSAVE;
}

uint16_t NeuroShield::getCat()
{
    uint16_t ret_val = spi.read(NM_CAT);
    POWERSAVE;
    return(ret_val);
}

// --------------------------------------------------------
// Get/Set the AIF register
//---------------------------------------------------------
void NeuroShield::setAif(uint16_t value)
{
    spi.write(NM_AIF, value);
    POWERSAVE;
}

uint16_t NeuroShield::getAif()
{
    uint16_t ret_val = spi.read(NM_AIF);
    POWERSAVE;
    return(ret_val);
}

// --------------------------------------------------------
// Get/Set the Minimum Influence Field register
//---------------------------------------------------------
void NeuroShield::setMinif(uint16_t value)
{
    spi.write(NM_MINIF, value);
    POWERSAVE;
}

uint16_t NeuroShield::getMinif()
{
    uint16_t ret_val = spi.read(NM_MINIF);
    POWERSAVE;
    return(ret_val);
}

// --------------------------------------------------------
// Get/Set the Maximum Influence Field register
//---------------------------------------------------------
void NeuroShield::setMaxif(uint16_t value)
{
    spi.write(NM_MAXIF, value);
    POWERSAVE;
}

uint16_t NeuroShield::getMaxif()
{
    uint16_t ret_val = spi.read(NM_MAXIF);
    POWERSAVE;
    return(ret_val);
}

// --------------------------------------------------------
// Get the Neuron Identifier
//---------------------------------------------------------
uint16_t NeuroShield::getNid()
{
    uint16_t ret_val = spi.read(NM_NID);
    POWERSAVE;
    return(ret_val);
}

// --------------------------------------------------------
// Get/Set the Global Context register
//---------------------------------------------------------
void NeuroShield::setGcr(uint16_t value)
{
    // GCR[15-8]= unused
    // GCR[7]= Norm (0 for L1; 1 for LSup)
    // GCR[6-0]= Active context value
    spi.write(NM_GCR, value);
    POWERSAVE;
}

uint16_t NeuroShield::getGcr()
{
    uint16_t ret_val = spi.read(NM_GCR);
    POWERSAVE;
    return(ret_val);
}

// --------------------------------------------------------
// Reset the chain to first neuron in SR Mode
//---------------------------------------------------------
void NeuroShield::resetChain()
{
    spi.write(NM_RSTCHAIN, 0);
    POWERSAVE;
}

// --------------------------------------------------------
// Get/Set the Network Status register
// bit 2 = UNC (read only)
// bit 3 = ID (read only)
// bit 4 = SR mode
// bit 5 = KNN mode
//---------------------------------------------------------
void NeuroShield::setNsr(uint16_t value)
{
    spi.write(NM_NSR, value);
    POWERSAVE;
}

uint16_t NeuroShield::getNsr()
{
    uint16_t ret_val = spi.read(NM_NSR);
    POWERSAVE;
    return(ret_val);
}

// --------------------------------------------------------
// Read the number of committed neurons
//---------------------------------------------------------
uint16_t NeuroShield::getNcount()
{
    uint16_t ret_val = spi.read(NM_NCOUNT);
    POWERSAVE;
    return(ret_val);
}

// --------------------------------------------------------
// Set the PowerSave mode
//---------------------------------------------------------
void NeuroShield::setPowerSave()
{
    spi.write(NM_POWERSAVE, 1);
    POWERSAVE;
}

// ------------------------------------------------------------ 
// Un-commit all the neurons, so they become ready to learn
// Reset the Maximum Influence Field to default value=0x4000
// ------------------------------------------------------------ 
void NeuroShield::forget()
{
    spi.write(NM_FORGET, 0);
    POWERSAVE;
}

// ------------------------------------------------------------ 
// Un-commit all the neurons, so they become ready to learn,
// Set the Maximum Influence Field (default value=0x4000)
// ------------------------------------------------------------ 
void NeuroShield::forget(uint16_t maxif)
{
    spi.write(NM_FORGET, 0);
    spi.write(NM_MAXIF, maxif);
    POWERSAVE;
}

// ------------------------------------------------------------ 
// Count total neurons in SR-mode
// ------------------------------------------------------------ 
void NeuroShield::countTotalNeurons()
{
    uint16_t read_cat;
    
    spi.write(NM_FORGET, 0);
    spi.write(NM_NSR, 0x0010);
    spi.write(NM_TESTCAT, 0x0001);
    spi.write(NM_RSTCHAIN, 0);
    
    total_neurons = 0;
    while (1) {
        read_cat = spi.read(NM_CAT);
        if (read_cat == 0xFFFF)
            break;
        total_neurons++;
    }
    spi.write(NM_NSR, 0x0000);
    spi.write(NM_FORGET, 0);
    POWERSAVE;
}

// --------------------------------------------------------------
// Un-commit all the neurons, so they become ready to learn,
// Set the Maximum Influence Field (default value=0x4000)
// Clear the memory of the neurons
// --------------------------------------------------------------
void NeuroShield::clearNeurons()
{
    spi.write(NM_FORGET, 0);
    spi.write(NM_NSR, 0x0010);
    spi.write(NM_TESTCAT, 1);
    spi.write(NM_NSR, 0x0000);
    for (int i = 0; i < NEURON_SIZE; i++) {
        spi.write(NM_INDEXCOMP, i);
        spi.write(NM_TESTCOMP, 0);
    }
    spi.write(NM_FORGET, 0);
    POWERSAVE;
}

// --------------------------------------------------------
// Broadcast a vector to the neurons and return the recognition status
// 0= unknown, 4=uncertain, 8=Identified
//---------------------------------------------------------
uint16_t NeuroShield::broadcast(uint8_t vector[], uint16_t length)
{
    uint16_t ret_val;
    spi.writeVector(vector, (length - 1));
    spi.write(NM_LCOMP, vector[length - 1]);
    ret_val = spi.read(NM_NSR);
    POWERSAVE;
    return(ret_val);
}

//-----------------------------------------------
// Learn a vector using the current context value
//----------------------------------------------
uint16_t NeuroShield::learn(uint8_t vector[], uint16_t length, uint16_t category)
{
    uint16_t ret_val;
    broadcast(vector, length);
    spi.write(NM_CAT, category);
    ret_val = spi.read(NM_NCOUNT);
    POWERSAVE;
    return(ret_val);
}

// ---------------------------------------------------------
// Classify a vector and return its classification status
// NSR=0, unknown
// NSR=8, identified
// NSR=4, uncertain
// ---------------------------------------------------------
uint16_t NeuroShield::classify(uint8_t vector[], uint16_t length)
{
    uint16_t ret_val;
    broadcast(vector, length);
    ret_val = spi.read(NM_NSR);
    POWERSAVE;
    return(ret_val);
}

//----------------------------------------------
// Recognize a vector and return the best match, or the 
// category, distance and identifier of the top firing neuron
//----------------------------------------------
uint16_t NeuroShield::classify(uint8_t vector[], uint16_t length, uint16_t* distance, uint16_t* category, uint16_t* nid)
{
    uint16_t ret_val;
    broadcast(vector, length);
    *distance = spi.read(NM_DIST);
    *category = spi.read(NM_CAT);
    *nid = spi.read(NM_NID);
    ret_val = spi.read(NM_NSR);
    POWERSAVE;
    return(ret_val);
}

//----------------------------------------------
// 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
//----------------------------------------------
uint16_t NeuroShield::classify(uint8_t vector[], uint16_t length, uint16_t k, uint16_t distance[], uint16_t category[], uint16_t nid[])
{
    uint16_t recog_nbr = 0;
    
    broadcast(vector, length);
    for (int i = 0; i < k; i++) {
        distance[i] = spi.read(NM_DIST);
        if (distance[i] == 0xFFFF) {
            category[i] = 0xFFFF;
            nid[i] = 0xFFFF;
        }
        else {
            recog_nbr++;
            category[i] = spi.read(NM_CAT);
            nid[i] = spi.read(NM_NID);
        }
    }
    POWERSAVE;
    return(recog_nbr);
}

// ------------------------------------------------------------ 
// Set a context and associated minimum and maximum influence fields
// ------------------------------------------------------------ 
void NeuroShield::setContext(uint8_t context)
{
    // context[15-8]= unused
    // context[7]= Norm (0 for L1; 1 for LSup)
    // context[6-0]= Active context value
    uint16_t read_val = spi.read(NM_GCR);
    read_val = (read_val & 0xFF80) | (context & 0x007F);
    spi.write(NM_GCR, read_val);
    POWERSAVE;
}

// ------------------------------------------------------------ 
// Set a context and associated minimum and maximum influence fields
// ------------------------------------------------------------ 
void NeuroShield::setContext(uint8_t context, uint16_t minif, uint16_t maxif)
{
    // context[15-8]= unused
    // context[7]= Norm (0 for L1; 1 for LSup)
    // context[6-0]= Active context value
    uint16_t read_val = spi.read(NM_GCR);
    read_val = (read_val & 0xFF80) | (context & 0x007F);
    spi.write(NM_GCR, read_val);
    spi.write(NM_MINIF, minif);
    spi.write(NM_MAXIF, maxif);
    POWERSAVE;
}

// ------------------------------------------------------------ 
// Get a context and associated minimum and maximum influence fields
// ------------------------------------------------------------ 
void NeuroShield::getContext(uint8_t* context, uint16_t* minif, uint16_t* maxif)
{
    // context[15-8]= unused
    // context[7]= Norm (0 for L1; 1 for LSup)
    // context[6-0]= Active context value
    *context = (uint8_t)(spi.read(NM_GCR) & 0x007F);
    *minif = spi.read(NM_MINIF);
    *maxif = spi.read(NM_MAXIF);
    POWERSAVE;
}

// --------------------------------------------------------
// Set the neurons in Radial Basis Function mode (default)
//---------------------------------------------------------
void NeuroShield::setRbfClassifier()
{
    uint16_t temp_nsr = spi.read(NM_NSR);
    spi.write(NM_NSR, (temp_nsr & 0x00DF));
    POWERSAVE;
}

// --------------------------------------------------------
// Set the neurons in K-Nearest Neighbor mode
//---------------------------------------------------------
void NeuroShield::setKnnClassifier()
{
    uint16_t temp_nsr = spi.read(NM_NSR);
    spi.write(NM_NSR, (temp_nsr | 0x0020));
    POWERSAVE;
}

//-------------------------------------------------------------
// Read the contents of the neuron pointed by index in the chain of neurons
// starting at index 1
//-------------------------------------------------------------
void NeuroShield::readNeuron(uint16_t nid, uint16_t model[], uint16_t* ncr, uint16_t* aif, uint16_t* cat)
{
    if (nid == 0) {
        *ncr = 0xFFFF;
        *aif = 0xFFFF;
        *cat = 0xFFFF;
        return;
    }
    
    uint16_t temp_nsr = spi.read(NM_NSR);
    spi.write(NM_NSR, 0x0010);
    spi.write(NM_RSTCHAIN, 0);
    if (nid > 1) {
        // move to index in the chain of neurons
        for (int i = 1; i < nid; i++)
            spi.read(NM_CAT);
    }
    
    *ncr = spi.read(NM_NCR);
    if (support_burst_read == 1) {
        spi.readVector16(model, NEURON_SIZE);
    }
    else {
        for (int i = 0; i < NEURON_SIZE; i++)
            model[i] = spi.read(NM_COMP);
    }
    *aif = spi.read(NM_AIF);
    *cat = spi.read(NM_CAT);
    spi.write(NM_NSR, temp_nsr);        // set the NN back to its calling status
    POWERSAVE;
}

//-------------------------------------------------------------
// Read the contents of the neuron pointed by index in the chain of neurons
// starting index is 1
// Returns an array of (NEURON_SIZE + 4) words with the following format
// NCR, NEURON_SIZE * COMP, AIF, MINIF, CAT
//-------------------------------------------------------------
void NeuroShield::readNeuron(uint16_t nid, uint16_t neuron[])
{
    if (nid == 0) {
        for (int i = 0; i < (NEURON_SIZE + 4); i++) {
            neuron[i] = 0xFFFF;
        }
        return;
    }
    
    uint16_t temp_nsr = spi.read(NM_NSR);
    spi.write(NM_NSR, 0x0010);
    spi.write(NM_RSTCHAIN, 0);
    if (nid > 1) {
        // move to index in the chain of neurons
        for (int i = 1; i < nid; i++)
            spi.read(NM_CAT);
    }
    
    neuron[0] = spi.read(NM_NCR);
    if (support_burst_read == 1) {
        spi.readVector16(&neuron[1], NEURON_SIZE);
    }
    else {
        for (int i = 0; i < NEURON_SIZE; i++)
            neuron[i + 1] = spi.read(NM_COMP);
    }
    neuron[NEURON_SIZE + 1] = spi.read(NM_AIF);
    neuron[NEURON_SIZE + 2] = spi.read(NM_MINIF);
    neuron[NEURON_SIZE + 3] = spi.read(NM_CAT);
    spi.write(NM_NSR, temp_nsr);        // set the NN back to its calling status
    POWERSAVE;
}

//----------------------------------------------------------------------------
// Read the contents of the committed neurons
// The output array has a dimension ncount * neurondata
// neurondata describes the content of a neuron and has a dimension (NEURON_SIZE + 4) words
// and with the following format NCR, NEURON_SIZE * COMP, AIF, MINIF, CAT
//----------------------------------------------------------------------------
uint16_t NeuroShield::readNeurons(uint16_t neurons[])
{
    uint32_t offset = 0;
    uint16_t ncount = spi.read(NM_NCOUNT);
    uint16_t temp_nsr = spi.read(NM_NSR);       // save value to restore NN status upon exit
    spi.write(NM_NSR, 0x0010);
    spi.write(NM_RSTCHAIN, 0);
    for (int i = 0; i < ncount; i++) {
        neurons[offset + 0] = spi.read(NM_NCR);
        if (support_burst_read == 1) {
            spi.readVector16(&neurons[offset + 1], NEURON_SIZE);
        }
        else {
            for (int j = 0; j < NEURON_SIZE; j++) {
                neurons[offset + 1 + j] = spi.read(NM_COMP);
            }
        }
        neurons[offset + 1 + NEURON_SIZE] = spi.read(NM_AIF);
        neurons[offset + 2 + NEURON_SIZE] = spi.read(NM_MINIF);
        neurons[offset + 3 + NEURON_SIZE] = spi.read(NM_CAT);
        offset += (NEURON_SIZE + 4);
    }
    spi.write(NM_NSR, temp_nsr);                // set the NN back to its calling status
    POWERSAVE;
    return(ncount);
}

void NeuroShield::readCompVector(uint16_t* data, uint16_t size)
{
    if (support_burst_read == 1) {
        spi.readVector16(data, size);
    }
    else {
        for (int i = 0; i < size; i++) {
            *data = spi.read(NM_COMP);
            data++;
        }
    }
    POWERSAVE;
}

//---------------------------------------------------------------------
// Clear the neurons and write their content from an input array
// The input array has a dimension ncount * neurondata
// neurondata describes the content of a neuron and has a dimension (NEURON_SIZE + 4) words
// and with the following format NCR, NEURON_SIZE * COMP, AIF, MINIF, CAT
//---------------------------------------------------------------------
void NeuroShield::writeNeurons(uint16_t neurons[], uint16_t ncount)
{
    uint32_t offset = 0;
    if (ncount > total_neurons)
        ncount = total_neurons;
    uint16_t temp_nsr = spi.read(NM_NSR);       // save value to restore NN status upon exit
    uint16_t temp_gcr = spi.read(NM_GCR);
    clearNeurons();
    spi.write(NM_NSR, 0x0010);
    spi.write(NM_RSTCHAIN, 0);
    for (int i = 0; i < ncount; i++) {
        spi.write(NM_NCR, neurons[offset + 0]);
        spi.writeVector16(&neurons[offset + 1], NEURON_SIZE);
        spi.write(NM_AIF, neurons[offset + 1 + NEURON_SIZE]);
        spi.write(NM_MINIF, neurons[offset + 2 + NEURON_SIZE]);
        spi.write(NM_CAT, neurons[offset + 3 + NEURON_SIZE]);
        offset += (NEURON_SIZE + 4);
    }
    spi.write(NM_NSR, temp_nsr);                // set the NN back to its calling status
    spi.write(NM_GCR, temp_gcr);
    POWERSAVE;
}

// --------------------------------------------------------
// Write N-component
//---------------------------------------------------------
void NeuroShield::writeCompVector(uint16_t* data, uint16_t size)
{
    spi.writeVector16(data, size);
    POWERSAVE;
}

// --------------------------------------------------------
// Get/Set the NM500 register value
//---------------------------------------------------------
uint16_t NeuroShield::testCommand(uint8_t read_write, uint8_t reg, uint16_t data)
{
    uint16_t ret_val = 0;
    if (read_write == 0) {
        ret_val = spi.read(reg);
    }
    else if (read_write == 1) {
        spi.write(reg, data);
    }
    POWERSAVE;
    return(ret_val);
}

// ----------------------------------------------------------------
// Get FPGA Version
// [15:8] board type : 00 = NeuroShield, 01 = Prodigy ...
// [ 7:4] board version
// [ 3:0] fpga version
// ----------------------------------------------------------------
uint16_t NeuroShield::fpgaVersion()
{
    return(spi.version());
}

// ----------------------------------------------------------------
// Excute NM500 SW Reset
// ----------------------------------------------------------------
void NeuroShield::nm500Reset()
{
    spi.reset();
}

// ----------------------------------------------------------------
// Select LED Scenario
// ----------------------------------------------------------------
void NeuroShield::ledSelect(uint8_t data)
{
    spi.ledSelect(data);
}