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

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

# Import standard modules
import sys
import time

# Import specific modules


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

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

class COpenCoresSPI:
        # OpenCores SPI registers description
        R_RX = [0x00, 0x04, 0x08, 0x0C]
	R_TX = [0x00, 0x04, 0x08, 0x0C]
	R_CTRL = 0x10
	R_DIV = 0x14
	R_SS = 0x18

	LGH_MASK = (0x7F)
	CTRL_GO = (1<<8)
        CTRL_BSY = (1<<8)
	CTRL_RXNEG = (1<<9)
	CTRL_TXNEG = (1<<10)
	CTRL_LSB = (1<<11)
       	CTRL_IE = (1<<12)
        CTRL_ASS = (1<<13)

        DIV_MASK = (0xFFFF)

        # Static variable
	conf = 0x0

        # Constant declaration
        SS_SEL = [0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40]
        WAIT_TIME_OUT = 2

	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)

	def __init__(self, bus, base_addr, divider):
		self.bus = bus;
		self.base_addr = base_addr;
		self.wr_reg(self.R_DIV, (divider & self.DIV_MASK));
		# Default configuration:
                # ASS = Automatic Slave Select
                # TXNEG = MOSI changes on SCLK falling edge
		self.conf = self.CTRL_ASS | self.CTRL_TXNEG

	def wait_busy(self):
                init_time=time.time()
                while(self.rd_reg(self.R_CTRL) & self.CTRL_BSY):
                        if (time.time()-init_time) > self.WAIT_TIME_OUT:
                                raise SPIDeviceOperationError(self.base_addr, "Wait timeout")

	def config(self, ass, rx_neg, tx_neg, lsb, ie):
		self.conf = 0
		if(ass):
			self.conf |= self.CTRL_ASS
		if(tx_neg):
			self.conf |= self.CTRL_TXNEG
		if(rx_neg):
			self.conf |= self.CTRL_RXNEG
		if(lsb):
			self.conf |= self.CTRL_LSB
		if(ie):
			self.conf |= self.CTRL_IE

	# slave = slave number (0 to 7)
	# data = byte data array to send,
        #        in case of read, filled with dummy data of the right size
	def transaction(self, slave, data):
		txrx = [0x00000000, 0x00000000, 0x00000000, 0x00000000]
		for i in range(0,len(data)):
			txrx[i/4] += (data[i]<<((i%4)*8))
			#print '[SPI] tx[%d]=%.8X data[%d]=%.2X' % (i/4,txrx[i/4],i,data[i])
		for i in range(0, len(txrx)):
			self.wr_reg(self.R_TX[i], txrx[i])
		#print '[SPI] data length: 0x%X' % len(data)
                if slave > len(self.SS_SEL):
                    raise SPIDeviceOperationError(self.base_addr, "Maximum number of slaves is %d" % len(self.SS_SEL))
		self.wr_reg(self.R_SS, self.SS_SEL[slave])
		self.wr_reg(self.R_CTRL, (self.LGH_MASK & (len(data)<<3)) | self.CTRL_GO | self.conf)
		self.wait_busy()
		for i in range(0, len(txrx)):
			txrx[i] = self.rd_reg(self.R_RX[i])
			#print '[SPI] rx[%d]=%.8X' % (i,txrx[i])
		return txrx

# Usage example
#gennum = rr.Gennum();
#spi = COpenCoresSPI(gennum, 0x80000, 500);
