#!   /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 time
import random
import math

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

# Import register maps
from svec_carrier_csr import *
from svec_irq_controller_regs 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
    I2C_ADDR = 0x1000
    ONEWIRE_ADDR = 0x1100
    CSR_ADDR = 0x1200
    IRQ_CONTROLLER_ADDR = 0x1300

    # Onewire core port
    DS18B20_PORT = 0

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

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

        # Objects declaration
        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.irq_controller = CCSR(self.bus, self.IRQ_CONTROLLER_ADDR, IRQ_CONTROLLER_REGS)

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

        # 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.")


    #======================================================================
    # 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)

    #======================================================================
    # 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)


    #======================================================================
    # Interrupt controller core

    # Print IRQ controller register map
    def print_irq_controller_regs(self):
        self.irq_controller.print_reg_map()

    # Set IRQ enable mask
    def set_irq_en_mask(self, mask):
        try:
            self.irq_controller.set_reg('ENABLE', mask)
            self.irq_controller.set_reg('DISABLE', ~mask)
            return self.irq_controller.get_reg('MASK')
        except CSRDeviceOperationError as e:
            raise FmcAdc100mSvecOperationError(e)

    # Get IRQ enable mask
    def get_irq_en_mask(self):
        try:
            return self.irq_controller.get_reg('MASK')
        except CSRDeviceOperationError as e:
            raise FmcAdc100mSvecOperationError(e)

    # Returns IRQ status
    def get_irq_status(self):
        try:
            return self.irq_controller.get_reg('STATUS')
        except CSRDeviceOperationError as e:
            raise FmcAdc100mSvecOperationError(e)

    # Clears interrupt
    def clear_interrupt(self, irq):
        try:
            self.irq_controller.set_reg('STATUS', irq)
            return self.irq_controller.get_reg('STATUS')
        except CSRDeviceOperationError as e:
            raise FmcAdc100mSvecOperationError(e)

    # Set acquisition end interrupt mask
    def set_irq_acq_end_mask(self, slot, value):
        try:
            if slot == 0:
                fmc = 'FMC0_ACQ_END'
            elif slot == 1:
                fmc = 'FMC1_ACQ_END'
            else:
                raise FmcAdc100mSvecOperationError("Slot number out of range [0:1]")
            self.irq_controller.set_field('ENABLE', fmc, value)
            self.irq_controller.set_field('DISABLE', fmc, ~value)
            return self.irq_controller.get_field('MASK', fmc)
        except CSRDeviceOperationError as e:
            raise FmcAdc100mSvecOperationError(e)

    # Set trigger interrupt mask
    def set_irq_trig_mask(self, slot, value):
        try:
            if slot == 0:
                fmc = 'FMC0_ACQ_TRG'
            elif slot == 1:
                fmc = 'FMC1_ACQ_TRG'
            else:
                raise FmcAdc100mSvecOperationError("Slot number out of range [0:1]")
            self.irq_controller.set_field('ENABLE', fmc, value)
            self.irq_controller.set_field('DISABLE', fmc, ~value)
            return self.irq_controller.get_field('MASK', fmc)
        except CSRDeviceOperationError as e:
            raise FmcAdc100mSvecOperationError(e)
