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

import sys
import rr
import random
import time
import math
import gn4124
import csr
import onewire
import ds18b20


class CSpecFmcAdc100Ms:


    GNUM_CSR_ADDR = 0x00000
    SPEC_ONEWIRE_ADDR = 0x20000
    SPEC_CSR_ADDR = 0x30000
    UTC_CORE_ADDR = 0x40000
    IRQ_CONTROLLER_ADDR = 0x50000

    # SPEC CSR
    SPEC_CSR={0x00:'Carrier type, PCB version',
              0x04:'Bitstream type',
              0x08:'Bitstream date',
              0x0C:'Status',
              0x10:'Control'}

    CSR_TYPE_VER = 0x00
    CSR_BSTM_TYPE = 0x04
    CSR_BSTM_DATE = 0x08
    CSR_STATUS = 0x0C
    CSR_CTRL = 0x10

    PCB_VER_MASK = 0x000F
    CARRIER_TYPE_MASK = 0xFFFF0000

    STATUS_FMC_PRES = (1<<0)
    STATUS_P2L_PLL_LCK = (1<<1)
    STATUS_SYS_PLL_LCK = (1<<2)
    STATUS_DDR3_CAL_DONE = (1<<3)

    CTRL_LED_GREEN = (1<<0)
    CTRL_LED_RED = (1<<1)
    CTRL_DAC_CLR_N = (1<<2)

    # UTC core
    UTC_CORE_SECONDS = 0x0
    UTC_CORE_COARSE = 0x4

    # tag = [wb_addr_offset, meta, second, coarse, fine]
    tag_trig = [0x8, 0, 0, 0, 0]
    tag_start = [0x18, 0, 0, 0, 0]
    tag_stop = [0x28, 0, 0, 0, 0]
    tag_end = [0x38, 0, 0, 0, 0]

    # IRQ controller
    IRQ_CTRL_MULT = 0x0
    IRQ_CTRL_SRC = 0x4
    IRQ_CTRL_EN_MASK = 0x8

    IRQ_SRC_DMA_END = 0x1
    IRQ_SRC_DMA_ERR = 0x2
    IRQ_SRC_ACQ_TRG = 0x4
    IRQ_SRC_ACQ_END = 0x8

    # Gennum interface
    DMA_LENGTH = 4096 # DMA length in bytes


    def __init__(self, bus):
        self.bus = bus
        # Objects declaration
        self.gnum = gn4124.CGN4124(self.bus, self.GNUM_CSR_ADDR)
        self.spec_onewire = onewire.COpenCoresOneWire(self.bus, self.SPEC_ONEWIRE_ADDR, 624, 124)
        self.ds18b20 = ds18b20.CDS18B20(self.spec_onewire, 0)
        self.spec_csr = csr.CCSR(self.bus, self.SPEC_CSR_ADDR)
        self.utc_core = csr.CCSR(self.bus, self.UTC_CORE_ADDR)
        self.irq_controller = csr.CCSR(self.bus, self.IRQ_CONTROLLER_ADDR)
        # Get physical addresses of the pages for DMA transfer
        self.dma_pages = self.gnum.get_physical_addr()

    def __del__(self):
        pass

    # print SPEC unique ID
    def print_unique_id(self):
        print('SPEC unique ID: %.12X') % self.ds18b20.read_serial_number()

    # print SPEC temperature
    def print_temp(self):
        serial_number = self.ds18b20.read_serial_number()
        print("SPEC temperature: %3.3f°C") % self.ds18b20.read_temp(serial_number)

    # Returns SPEC unique ID
    def get_unique_id(self):
        return self.ds18b20.read_serial_number()

    # Returns SPEC temperature
    def get_temp(self):
        serial_number = self.ds18b20.read_serial_number()
        if(serial_number == -1):
            return -1
        else:
            return self.ds18b20.read_temp(serial_number)

    # Set UTC core with current computer time
    def set_utc_time(self):
        current_time = time.time()
        utc_seconds = int(current_time)
        self.utc_core.wr_reg(self.UTC_CORE_SECONDS, utc_seconds)
        utc_coarse = int((current_time - utc_seconds)/8E-9)
        self.utc_core.wr_reg(self.UTC_CORE_COARSE, utc_coarse)
        return curent_time

    # Returns UTC seconds counter value
    def get_utc_second_cnt(self):
        return self.utc_core.rd_reg(self.UTC_CORE_SECONDS)

    # Set UTC seconds counter
    def set_utc_second_cnt(self, value):
        self.utc_core.wr_reg(self.UTC_CORE_SECONDS, value)
        return self.utc_core.rd_reg(self.UTC_CORE_SECONDS)

    # Returns UTC coarse counter value
    def get_utc_coarse_cnt(self):
        return self.utc_core.rd_reg(self.UTC_CORE_COARSE)

    # Set UTC coarse counter
    def set_utc_coarse_cnt(self, value):
        self.utc_core.wr_reg(self.UTC_CORE_COARSE, value)
        return self.utc_core.rd_reg(self.UTC_CORE_COARSE)

    # Returns last trigger event time-tag
    def get_utc_trig_tag(self):
        # Get metadata
        addr = self.tag_trig[0]
        self.tag_trig[1] = self.utc_core.rd_reg(addr)
        # Get seconds
        addr = self.tag_trig[0]+0x4
        self.tag_trig[2] = self.utc_core.rd_reg(addr)
        # Get coarse
        addr = self.tag_trig[0]+0x8
        self.tag_trig[3] = self.utc_core.rd_reg(addr)
        # Get fine
        addr = self.tag_trig[0]+0xc
        self.tag_trig[4] = self.utc_core.rd_reg(addr)

        return self.tag_trig

    # Returns last acquisition start event time-tag
    def get_utc_start_tag(self):
        # Get metadata
        addr = self.tag_start[0]
        self.tag_start[1] = self.utc_core.rd_reg(addr)
        # Get seconds
        addr = self.tag_start[0]+0x4
        self.tag_start[2] = self.utc_core.rd_reg(addr)
        # Get coarse
        addr = self.tag_start[0]+0x8
        self.tag_start[3] = self.utc_core.rd_reg(addr)
        # Get fine
        addr = self.tag_start[0]+0xc
        self.tag_start[4] = self.utc_core.rd_reg(addr)

        return self.tag_start

    # Returns last acquisition stop event time-tag
    def get_utc_stop_tag(self):
        # Get metadata
        addr = self.tag_stop[0]
        self.tag_stop[1] = self.utc_core.rd_reg(addr)
        # Get seconds
        addr = self.tag_stop[0]+0x4
        self.tag_stop[2] = self.utc_core.rd_reg(addr)
        # Get coarse
        addr = self.tag_stop[0]+0x8
        self.tag_stop[3] = self.utc_core.rd_reg(addr)
        # Get fine
        addr = self.tag_stop[0]+0xc
        self.tag_stop[4] = self.utc_core.rd_reg(addr)

        return self.tag_stop

    # Returns last acquisition end event time-tag
    def get_utc_end_tag(self):
        # Get metadata
        addr = self.tag_end[0]
        self.tag_end[1] = self.utc_core.rd_reg(addr)
        # Get seconds
        addr = self.tag_end[0]+0x4
        self.tag_end[2] = self.utc_core.rd_reg(addr)
        # Get coarse
        addr = self.tag_end[0]+0x8
        self.tag_end[3] = self.utc_core.rd_reg(addr)
        # Get fine
        addr = self.tag_end[0]+0xc
        self.tag_end[4] = self.utc_core.rd_reg(addr)

        return self.tag_end

    # Set IRQ enable mask
    def set_irq_en_mask(self, mask):
        self.irq_controller.wr_reg(self.IRQ_CTRL_EN_MASK, mask)
        return self.irq_controller.rd_reg(self.IRQ_CTRL_EN_MASK)

    # Get IRQ enable mask
    def get_irq_en_mask(self):
        return self.irq_controller.rd_reg(self.IRQ_CTRL_EN_MASK)

    # Returns multiple IRQ status
    def get_irq_mult(self):
        return self.irq_controller.rd_reg(self.IRQ_CTRL_MULT)

    # Clears multiple IRQ status
    def clear_irq_mult(self, irq):
        self.irq_controller.wr_reg(self.IRQ_CTRL_MULT, irq)
        return self.irq_controller.rd_reg(self.IRQ_CTRL_MULT)

    # Returns IRQ source
    def get_irq_source(self):
        return self.irq_controller.rd_reg(self.IRQ_CTRL_SRC)

    # Clears IRQ source
    def clear_irq_source(self, irq):
        self.irq_controller.wr_reg(self.IRQ_CTRL_SRC, irq)
        return self.irq_controller.rd_reg(self.IRQ_CTRL_SRC)

    # Print CSR
    def print_csr(self):
        print("\nCarrier configuration/status")
        for i in range(0,0x14,4):
            print("%31s: 0x%.8X (%d)") % (self.SPEC_CSR[i],self.spec_csr.rd_reg(i),self.spec_csr.rd_reg(i))

    # Make a DMA transfer
    #   carrier_addr and length are in bytes
    def get_data(self, carrier_addr, length):
        # Configure DMA
        items_required = int(math.ceil(length/float(self.DMA_LENGTH)))
        if(128 < items_required):
            print('Required items: %d')%items_required
            raise Exception('Current gn4124 class only supports up to 128 items.')
        #print('Required items: %d')%items_required
        for num in range(items_required):
            if(items_required == num+1):
                next_item = 0
                item_length = (length-(num*self.DMA_LENGTH))
            else:
                next_item = 1
                item_length = self.DMA_LENGTH
            if(0 == num):
                item_start_addr = carrier_addr
            else:
                item_start_addr = carrier_addr + (num*self.DMA_LENGTH)
            #print("item nb:%d item_carrier_addr:0x%.8X item_host_addr:0x%.8X item_length:%d next_item:%d)")%(num,item_start_addr,self.dma_pages[num+1],item_length,next_item)
            self.gnum.add_dma_item(item_start_addr, self.dma_pages[num+1], item_length, 0, next_item)
        items = self.gnum.get_memory_page(0)
        #print('DMA items:')
        #for i in range(items_required*8):
        #    print('%.4X: %.8X')%(i*4,items[i])
        # Start DMA
        self.gnum.start_dma()
        # Wait for end of DMA interrupt
        #print('Wait GN4124 interrupt')
        self.gnum.wait_irq()
        #print('GN4124 interrupt occured')
        dma_finished = 0
        #print('irq mask:%.4X')%self.get_irq_en_mask()
        while(0 == dma_finished):
            irq_src = self.get_irq_source()
            #print('IRQ source : %.4X')%irq_src
            #print('DMA status: %s')%self.gnum.get_dma_status()
            if(irq_src & self.IRQ_SRC_DMA_END):
                #print('IRQ source : %.4X')%irq_src
                self.clear_irq_source(self.IRQ_SRC_DMA_END)
                #print('IRQ source : %.4X')%self.get_irq_source()
                dma_finished = 1
            time.sleep(0.5)
        #print('DMA finished!')
        # Retrieve data from host memory
        data = []
        for i in range(items_required):
            data += self.gnum.get_memory_page(i+1)
        #print('data length:%d')%(len(data)*4)
        channels_data = []
        for i in range(length/4):
            channels_data.append(data[i] & 0xFFFF)
            channels_data.append(data[i]>>16)
        #print('channels data length:%d')%(len(channels_data))
        return channels_data

    # Wait end of acquisition
    def wait_end_acq(self):
        acq_end = 0
        while(0 == acq_end):
            irq_src = self.get_irq_source()
            print('IRQ source : %.4X')%irq_src
            if(irq_src & self.IRQ_SRC_ACQ_END):
                self.clear_irq_source(self.IRQ_SRC_ACQ_END)
                print('IRQ source : %.4X')%self.get_irq_source()
                acq_end = 1
        print('ACQ finished!')
