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

# Copyright CERN, 2013
# Author: Matthieu Cattin (CERN)
# Licence: GPL v2 or later.
# Website: http://www.ohwr.org
# Last modifications: 8/5/2012

# Import standard modules
import sys
import os
import time
import random
import math

# Add common modules and libraries location to path
cdir = os.path.dirname(os.path.realpath(__file__))
sys.path.append(os.path.join(cdir,'../pts/'))
sys.path.append(os.path.join(cdir,'../pts/common/'))
sys.path.append(os.path.join(cdir,'../../svec_pts/ubuntu/pts/pyts/'))

# Import specific modules
from sdb import *
from csr import *
from onewire import *
from gn4124 import *
from ds18b20 import *
from i2c import *
from vic import *

# Import register maps
from svec_carrier_csr import *

# Global constants
LED_COLOR = {"OFF":0x0, "GREEN":0x1, "RED":0x2, "ORANGE":0x3}


# Class to access fmcadc100m14b4cha SVEC specific Wishbone cores

class FmcAdc100mSvecOperationError(Exception):
    def __init__(self, msg):
        self.msg = msg
    def __str__(self):
        return ("FmcAdc100mSvec: %s" %(self.msg))

class CFmcAdc100mSvec:

    # Wishbone core base addresses
    SDB_ADDR = 0x0
    I2C_ADDR = 0x1000
    ONEWIRE_ADDR = 0x1100
    CSR_ADDR = 0x1200
    VIC_ADDR = 0x1300

    # Onewire core port
    DS18B20_PORT = 0

    #======================================================================
    # Class initialisation

    def __init__(self, bus, bs_type):
        self.bus = bus

        # Objects declaration
        self.sdb_csr = CCSR(self.bus, self.SDB_ADDR)
        self.sdb = CSDB(self.sdb_csr)
        self.i2c = COpenCoresI2C(self.bus, self.I2C_ADDR, 249)
        self.onewire = COpenCoresOneWire(self.bus, self.ONEWIRE_ADDR, 624, 124)
        self.ds18b20 = CDS18B20(self.onewire, self.DS18B20_PORT)
        self.csr = CCSR(self.bus, self.CSR_ADDR, CARRIER_CSR)
        self.vic = CVic(self.bus, self.VIC_ADDR)

        # Check if bitstream is properly loaded by reading a register
        ct = self.get_carrier_type()
        if(ct == 0xFFFFFFFF):
            raise FmcAdc100mSvecOperationError("Bitstream not properly loaded.")

        # Configure VIC
        #self.vic.print_regs()
        self.vic.set_polarity(1) # output active high
        #self.vic.enable_emu_edge(750) # emulate edge sensitive output (pulse width = 750 * 1/sys_clk = 6us)
        self.vic.enable_int(0) # enable interrupts from fmc0 eic
        self.vic.enable_int(1) # enable interrupts from fmc1 eic
        self.vic.enable_module()
        self.fmc_eic_addr = []
        self.fmc_eic_addr.append(self.vic.get_vector_addr(0))
        self.fmc_eic_addr.append(self.vic.get_vector_addr(1))

        # TODO
        # Check if the expected bitstream loaded
        #bs = self.get_bitstream_type()
        #if(bs != bs_type):
        #    raise FmcAdc100mSvecOperationError("Wrong bitsream. Excpect:0x%08X, Read:0x%08X" % (bs_type, bs))

        # Ckeck if a mezzanine is present
        if(not self.get_fmc_presence(0)):
            raise FmcAdc100mSvecOperationError("Mezzanine in slot 1 not present or PRSNT_M2C_L line faulty.")
        if(not self.get_fmc_presence(1)):
            raise FmcAdc100mSvecOperationError("Mezzanine in slot 2 not present or PRSNT_M2C_L line faulty.")


    #======================================================================
    # SDB records

    # Dump SDB records
    def sdb_dump(self):
        self.sdb.dump()


    #======================================================================
    # Control and status registers (CSR)

    # Print carrier CSR register map
    def print_csr(self):
        self.csr.print_reg_map()

    # Get carrier type
    def get_carrier_type(self):
        try:
            return self.csr.get_field('CARRIER', 'TYPE')
        except CSRDeviceOperationError as e:
            raise FmcAdc100mSvecOperationError(e)

    # Get PCB revision
    def get_pcb_rev(self):
        try:
            return self.csr.get_field('CARRIER', 'PCB_REV')
        except CSRDeviceOperationError as e:
            raise FmcAdc100mSvecOperationError(e)

    # Get status register value
    def get_status(self):
        try:
            return self.csr.get_reg('STAT')
        except CSRDeviceOperationError as e:
            raise FmcAdc100mSvecOperationError(e)

    # Get control register value
    def get_control(self):
        try:
            return self.csr.get_reg('CTRL')
        except CSRDeviceOperationError as e:
            raise FmcAdc100mSvecOperationError(e)

    # Get FMC presence state
    # 1 = mezzanine present, 0 = no nezzanine
    def get_fmc_presence(self, slot):
        try:
            if slot == 0:
                fmc = 'FMC0_PRES'
            elif slot == 1:
                fmc = 'FMC1_PRES'
            else:
                raise FmcAdc100mSvecOperationError("Slot number out of range [0:1]")
            return not self.csr.get_field('STAT', fmc)
        except CSRDeviceOperationError as e:
            raise FmcAdc100mSvecOperationError(e)

    # Get system clock PLL lock state
    def get_sys_pll_lock(self):
        try:
            return self.csr.get_field('STAT', 'SYS_PLL_LCK')
        except CSRDeviceOperationError as e:
            raise FmcAdc100mSvecOperationError(e)

    # Get DDR3 controller calibration done state
    def get_ddr0_cal_done(self, ddr):
        try:
            if ddr == 0:
                val = 'DDR0_CAL_DONE'
            elif ddr == 1:
                val = 'DDR1_CAL_DONE'
            else:
                raise FmcAdc100mSvecOperationError("DDR number out of range [0:1]")
            return self.csr.get_field('STAT', val)
        except CSRDeviceOperationError as e:
            raise FmcAdc100mSvecOperationError(e)

    # Set front panel LEDs
    def set_led(self, led, color):
        try:
            val = self.csr.get_field('CTRL', 'LED')
            val &= ~(0x3 << 2*(led-1))
            val |= (0x3 & LED_COLOR[color]) << 2*(led-1)
            return self.csr.set_field('CTRL', 'LED', val)
        except CSRDeviceOperationError as e:
            raise FmcAdc100mSvecOperationError(e)

    # Mezzanine software reset
    def sw_rst(self, slot):
        try:
            if slot == 0:
                self.csr.set_field('RST', 'FMC0', 0)
                time.sleep(0.001)
                self.csr.set_field('RST', 'FMC0', 1)
                time.sleep(0.001)
            elif slot == 1:
                self.csr.set_field('RST', 'FMC1', 0)
                time.sleep(0.001)
                self.csr.set_field('RST', 'FMC1', 1)
                time.sleep(0.001)
            else:
                raise FmcAdc100mSvecOperationError("Slot number out of range [0:1]")
        except CSRDeviceOperationError as e:
            raise FmcAdc100mSpecOperationError(e)

    # Set mezzanine software reset state
    def set_sw_rst(self, slot, value):
        try:
            if slot == 0:
                return self.csr.set_field('RST', 'FMC0', value)
            elif slot == 1:
                return self.csr.set_field('RST', 'FMC1', value)
            else:
                raise FmcAdc100mSvecOperationError("Slot number out of range [0:1]")
        except CSRDeviceOperationError as e:
            raise FmcAdc100mSpecOperationError(e)

    # Get mezzanine software reset state
    def get_sw_rst(self, slot):
        try:
            if slot == 0:
                return self.csr.get_field('RST', 'FMC0')
            elif slot == 1:
                return self.csr.get_field('RST', 'FMC1')
            else:
                raise FmcAdc100mSvecOperationError("Slot number out of range [0:1]")
        except CSRDeviceOperationError as e:
            raise FmcAdc100mSpecOperationError(e)

    #======================================================================
    # Onewire thermometer and unique ID

    # print SVEC unique ID
    def print_unique_id(self):
        try:
            print('SVEC unique ID: %.12X') % self.ds18b20.read_serial_number()
        except DS18B20OperationError as e:
            raise FmcAdc100mSvecOperationError(e)

    # print SVEC temperature
    def print_temp(self):
        try:
            serial_number = self.ds18b20.read_serial_number()
            print("SVEC temperature: %3.3f°C") % self.ds18b20.read_temp(serial_number)
        except DS18B20OperationError as e:
            raise FmcAdc100mSvecOperationError(e)

    # Returns SVEC unique ID
    def get_unique_id(self):
        try:
            return self.ds18b20.read_serial_number()
        except DS18B20OperationError as e:
            raise FmcAdc100mSvecOperationError(e)

    # Returns SVEC temperature
    def get_temp(self):
        try:
            serial_number = self.ds18b20.read_serial_number()
            return self.ds18b20.read_temp(serial_number)
        except DS18B20OperationError as e:
            raise FmcAdc100mSvecOperationError(e)


    #======================================================================
    # Vectored Interrupt Controller (VIC)

    # Get current interrupt vector address
    def vic_get_current_vector_addr(self):
        return self.vic.get_int_vector_addr()

    # Acknowledge current vector
    def vic_ack_current_vector(self):
        self.vic.int_ack()

    # Get raw interrupt status
    def vic_get_raw_status(self):
        return self.vic.get_raw_int_status()
