#!/usr/bin/python

# Copyright CERN, 2011
# Author: Matthieu Cattin (CERN)
# Author (modifications): Richard Carrillo <rcarrillo(AT)sevensols.com>
# Licence: GPL v2 or later.
# Website: http://www.ohwr.org
# Website: http://www.sevensols.com
# Last modifications: 26/4/2012

# Import standard modules
import sys
import time

# Import specific modules


# Class to access the wishbone to onewire master module from OpenCores
# http://opencores.org/project,sockit_owm

class OneWireDeviceOperationError(Exception):
    def __init__(self, addr, msg):
        self.msg = msg
        self.addr = addr
    def __str__(self):
        return ("OneWire [Wishbone core base address:0x%08X]: %s" %(self.addr, self.msg))

class COpenCoresOneWire:

    # OpenCores 1-wire registers description
    R_CSR = 0x0
    R_CDR = 0x4

    CSR_DAT_MSK = (1<<0)
    CSR_RST_MSK = (1<<1)
    CSR_OVD_MSK = (1<<2)
    CSR_CYC_MSK = (1<<3)
    CSR_PWR_MSK = (1<<4)
    CSR_IRQ_MSK = (1<<6)
    CSR_IEN_MSK = (1<<7)
    CSR_SEL_OFS = 8
    CSR_SEL_MSK = (0xF<<8)
    CSR_POWER_OFS = 16
    CSR_POWER_MSK = (0xFFFF<<16)

    CDR_NOR_MSK = (0xFFFF<<0)
    CDR_OVD_OFS = 16
    CDR_OVD_MSK = (0XFFFF<<16)

    # Constants declaration
    WAIT_TIME_OUT = 2
    MAX_BLOCK_LEN = 160

    def wr_reg(self, addr, val):
        self.bus.iwrite(0, self.base_addr +  addr, 4, val)

    def rd_reg(self,addr):
        return self.bus.iread(0, self.base_addr + addr, 4)

    # Initialise the onewire master module
    #   bus = host bus (PCIe, VME, etc...)
    #   base_addr = 1-wire core base address
    #   clk_div_nor = clock divider normal operation, clk_div_nor = Fclk * 5E-6 - 1
    #   clk_div_ovd = clock divider overdrive operation, clk_div_ovd = Fclk * 1E-6 - 1
    def __init__(self, bus, base_addr, clk_div_nor, clk_div_ovd):
        self.bus = bus
        self.base_addr = base_addr
        data = ((clk_div_nor & self.CDR_NOR_MSK) | ((clk_div_ovd<<self.CDR_OVD_OFS) & self.CDR_OVD_MSK))
        self.wr_reg(self.R_CDR, data)

    def reset(self, port):
        data = ((port<<self.CSR_SEL_OFS) & self.CSR_SEL_MSK) | self.CSR_CYC_MSK | self.CSR_RST_MSK
        self.wr_reg(self.R_CSR, data)
        init_time=time.time()
        while(self.rd_reg(self.R_CSR) & self.CSR_CYC_MSK):
            if (time.time()-init_time) > self.WAIT_TIME_OUT:
                raise OneWireDeviceOperationError(self.base_addr, "Wait timeout")
        reg = self.rd_reg(self.R_CSR)
        if not (~reg & self.CSR_DAT_MSK):
            raise OneWireDeviceOperationError(self.base_addr, "No presence pulse detected")

    def slot(self, port, bit):
        data = ((port<<self.CSR_SEL_OFS) & self.CSR_SEL_MSK) | self.CSR_CYC_MSK | (bit & self.CSR_DAT_MSK)
        self.wr_reg(self.R_CSR, data)
        init_time=time.time()
        while(self.rd_reg(self.R_CSR) & self.CSR_CYC_MSK):
            if (time.time()-init_time) > self.WAIT_TIME_OUT:
                raise OneWireDeviceOperationError(self.base_addr, "Wait timeout")
        reg = self.rd_reg(self.R_CSR)
        return reg & self.CSR_DAT_MSK

    def read_bit(self, port):
        return self.slot(port, 0x1)

    def write_bit(self, port, bit):
        return self.slot(port, bit)

    def read_byte(self, port):
        data = 0
        for i in range(8):
            data |= self.read_bit(port) << i
        return data

    def write_byte(self, port, byte):
        data = 0
        byte_old = byte
        for i in range(8):
            data |= self.write_bit(port, (byte & 0x1)) << i
            byte >>= 1
        if(byte_old != data):
            raise OneWireDeviceOperationError(self.base_addr, "Error while checking written byte")

    def write_block(self, port, block):
        if(self.MAX_BLOCK_LEN < len(block)):
            raise OneWireDeviceOperationError(self.base_addr, "Block too long")
        data = []
        for i in range(len(block)):
            data.append(self.write_byte(port, block[i]))
        return data

    def read_block(self, port, length):
        if(self.MAX_BLOCK_LEN < length):
            raise OneWireDeviceOperationError(self.base_addr, "Block too long")
        data = []
        for i in range(length):
            data.append(self.read_byte(port))
        return data
