#!/usr/bin/python
#coding: utf8

# Copyright CERN, 2011 - 2013 (INCAA Computers BV)
# Author: Samuel Iglesias Gonsalvez <siglesia@cern.ch>
# Author: Julian Lewis (Julian.Lewis@cern.ch)
# Author: INCAA Computers BV (info@incaacomputers.com)
# Licence: GPL v2 or later.
# Website: http://www.ohwr.org
# Website: http://www.incaacomputers.com
# Version: 2.0 (Last modifications: 13/06/2013)
#
# FPGA firmware: spec_pts_fmcsupply.bin
#
# Description:
#  Testing of the power supplies on the FMC connector of the SPEC board. For
#  this test an "FMC tester mezzanine board" (EDA-02327) needs to be plugged in the
#  FMC slot. The AD7997 ADC on the tester mezzanine is taking measurements of the
#  voltage levels of the power supplies and can communicate through an I2C interface.
#  The firmware loaded to the FPGA implements the interface for the
#  communication with the ADC.
#
#  The test checks the voltage on the power pins of the FMC connector and verifies
#  if the received values are within tolerance limits:
#    P2V5_FMC tolerance limits [2.37V,  2.62V]
#    P3V3_FMC tolerance limits [3.13V,  3.47V]
#    P12V_FMC tolerance limits [11.2V, 12.78V]

import sys
import rr
import time
import os

from i2c import COpenCoresI2C
from csr import *
# Import register maps
from carrier_csr import *

from ptsexcept import *

"""
test01: checks voltage of the power pins in FMC connector.
"""

##-------------------------------------------------------------------------------------------------
##                                       ADC AD7997 class                                        --
##-------------------------------------------------------------------------------------------------

class ADC_AD7997:
    CONVER_R = 0x00;
    CONFIG_R = 0x02;
    CH8 = (1 << 11);
    CH7 = (1 << 10);
    CH6 = (1 << 9);
    CH5 = (1 << 8);
    CH4 = (1 << 7);
    CH3 = (1 << 6);
    CH2 = (1 << 5);
    CH1 = (1 << 4);

    def __init__(self, i2c, addr):
        self.i2c = i2c;
        self.addr = addr;

    def wr_reg16(self, addr, value):
        self.i2c.start(self.addr, True);
        self.i2c.write(addr, False);
        tmp = (value >> 8) & 0xFF;
        self.i2c.write(value, False);
        tmp = value & 0xFF;
        self.i2c.write(value, True)

    def wr_reg8(self, addr, value):
        self.i2c.start(self.addr, True); # write cycle
        self.i2c.write(addr, False);
        self.i2c.write(value, True);    

    def rd_reg16(self, addr):
        self.i2c.start(self.addr, True);
        self.i2c.write(addr, False);
        self.i2c.start(self.addr, False);
        tmp_MSB = self.i2c.read(False);
        tmp_LSB = self.i2c.read(True);
        value = (tmp_MSB << 8) | tmp_LSB;
        return value;

    def rd_reg8(self, addr):
        self.i2c.start(self.addr, True);
        self.i2c.write(addr, False);
        self.i2c.start(self.addr, False);
        return self.i2c.read(True);    

def adc_value(value) :
    return str(2.495 * (0x3FF & (value >> 2)) / 1024); # 10 bits

def adc_convert_to_real_value (nr_value, value) :
    ratio = {'8': 0.6666, '7': 0.1666, '6': 0.52381};
    return str(float(value) / ratio[str(nr_value)]) 

##-------------------------------------------------------------------------------------------------
##                                             main                                              --
##-------------------------------------------------------------------------------------------------

def main (default_directory='.'):
    # Configure the FPGA using the program fpga_loader
    path_fpga_loader = '../firmwares/fpga_loader';
    path_firmware = '../firmwares/spec_pts_fmcsupply.bin';
    firmware_loader = os.path.join(default_directory, path_fpga_loader)
    bitstream = os.path.join(default_directory, path_firmware)
    print "Loading firmware: %s" % (firmware_loader + ' ' + bitstream)
    os.system( firmware_loader + ' ' + bitstream)
    #time.sleep(2);

    # Load board library and open the corresponding device
    print "Loading hardware access library and opening devices\n"
    spec = rr.Gennum();
    
    spec_csr = CCSR(spec, 0x10000, CARRIER_CSR);
    if spec_csr.get_field('CARRIER', 'TYPE') != 0x0001 :
        spec_csr.print_reg_map()
        raise PtsCritical("CRITICAL: Carrier is not a SPEC board")
    pcb_rev = spec_csr.get_field('CARRIER', 'PCB_REV')
    if pcb_rev < 3 :
        raise PtsError("ERROR: SPEC PCB board not of a supported revision")
    else :
        print "SPEC board PCB Rev: %d\n" % pcb_rev
        tmp_stdout = sys.stdout;
        sys.stdout = sys.__stdout__;
        print "SPEC board PCB Rev: %d\n" % pcb_rev
        sys.stdout = tmp_stdout;
    
    i2c = COpenCoresI2C(spec, 0x40000, 99); # Prescaler value calculated using 50 MHz clock
    
    # The address of the AD7997 is 0x23, pin 15 connected to GND.
    adc = ADC_AD7997(i2c, 0x23);
    
    print "Test01 Start"
    init_test_time = time.time()
    ret_error = None
    
    # Retrieving the voltage measurements
    
    value8 = adc_value(adc.rd_reg16(0xF0));  # P2V5_FMC
    value7 = adc_value(adc.rd_reg16(0xE0));  # P12V_FMC
    value6 = adc_value(adc.rd_reg16(0xD0));  # P3V3_FMC
    value5 = adc_value(adc.rd_reg16(0xC0));
    value4 = adc_value(adc.rd_reg16(0xB0));
    value3 = adc_value(adc.rd_reg16(0xA0));
    value2 = adc_value(adc.rd_reg16(0x90));
    value1 = adc_value(adc.rd_reg16(0x80));

    ret_error=""
    # Evaluating the results
    # P2V5_FMC acceptable limits: >2.37V and <2.62V

    if(float(value8) < 1.58) or (float(value8) > 1.75) :
        msg = "ERROR: P2V5_FMC: %sV is out of range[2.37V .. 2.62V]" % (adc_convert_to_real_value(8, value8))
        print(msg)
        ret_error = msg
    else:
        msg = "P2V5_FMC: %sV OK" % (adc_convert_to_real_value(8, value8))
        print("%s\n" % (msg))

    # P3V3_FMC acceptable limits: >3.13V and <3.47V

    if(float(value6) < 1.64) or (float(value6) > 1.82):
        msg = "ERROR: P3V3_FMC: %sV is out of range[3.13V .. 3.47V]" % (adc_convert_to_real_value(6, value6))
        print(msg)
        ret_error = ret_error + "\n" + msg
    else:
        msg = "P3V3_FMC: %sV OK" % (adc_convert_to_real_value(6, value6))
        print("%s\n" % (msg))

    # P12V_FMC acceptable limits: >11.2V and <12.78V

    if(float(value7) < 1.88) or (float(value7) > 2.13):
        msg = "ERROR: P12V_FMC: %sV is out of range[11.2V .. 12.78V]" % (adc_convert_to_real_value(7, value7))
        print(msg)
        ret_error = ret_error + "\n" + msg
    else:
        msg = "P12V_FMC: %sV OK" % (adc_convert_to_real_value(7, value7))
        print("%s\n" % (msg))

    end_test_time = time.time()
    print "End of Test01"
    print "RESULT: [{}]".format("FAIL" if ret_error else "OK")
    print 'Test01 elapsed time: {:.2f} seconds'.format(end_test_time-init_test_time)
    if ret_error:
        raise PtsError(ret_error)
    
if __name__ == '__main__' :
    main();

