Add library for talking to AtoI devboard
authorDan White <dan@whiteaudio.com>
Sun, 8 Apr 2012 20:32:29 +0000 (15:32 -0500)
committerDan White <dan@whiteaudio.com>
Sun, 8 Apr 2012 20:32:29 +0000 (15:32 -0500)
python-lib/usbio.py [new file with mode: 0644]

diff --git a/python-lib/usbio.py b/python-lib/usbio.py
new file mode 100644 (file)
index 0000000..b0ee15a
--- /dev/null
@@ -0,0 +1,322 @@
+#!/usr/bin/env python
+
+#from array import array
+#import struct
+#import time
+
+from myhdl import intbv
+
+#from pyftdi.pyftdi.ftdi import Ftdi
+#from pyftdi.pyftdi.spi import SpiController
+
+
+
+#   SPI interface
+#       48-bit harmonic control data
+#           3x16 or 6x8 words
+#           myhdl intbv to set/access data??
+#
+#           NCO
+#           47 - cal
+#           46 - rst
+#           45..32 - fcw
+#
+#           OTA-A
+#           31 - cintA
+#           30 - zeroA 
+#           29 - seA
+#           28 - fastA
+#           27..16 - tuneA
+#
+#           OTA-B
+#           15 - cintB
+#           14 - zeroB
+#           13 - seB
+#           12 - fastB
+#           11..0 - tuneb
+#               11..8 - gain
+#               7..0  - offset
+
+class NCO(object):
+    RST_POS = 14
+    FCW_WIDTH = 14
+
+    def __init__(self, fcw=0, rst=0):
+        self._word = intbv(0)[15:]
+        self.fcw = fcw
+        self.rst = rst
+
+    @property
+    def word(self):
+        return self._word
+
+    @property
+    def rst(self):
+        return ((self.word >> self.RST_POS) & 0b1)
+
+    @rst.setter
+    def rst(self, value):
+        if value:
+            v = 1
+        else:
+            v = 0
+        self._word[self.RST_POS] = v
+
+    @property
+    def fcw(self):
+        return self.word[self.FCW_WIDTH:]
+
+    @fcw.setter
+    def fcw(self, value):
+        v = intbv(value, max=2**self.FCW_WIDTH)
+        self._word[self.FCW_WIDTH:] = v
+
+
+class OTA(object):
+    CINT_POS = 15
+    ZERO_POS = 14
+    SE_POS = 13
+    FAST_POS = 12
+    TUNE_WIDTH = 12
+    GAIN_SHIFT = 8
+    GAIN_WIDTH = 4
+    OFFSET_SHIFT = 0
+    OFFSET_WIDTH = 8
+
+    def __init__(self, cint=0, zero=0, se=0, fast=0, gain=0, offset=0):
+        self._word = intbv(0)[16:]
+        self.cint = cint
+        self.zero = zero
+        self.se = se
+        self.fast = fast
+        self.gain = gain
+        self.offset = offset
+
+    @property
+    def word(self):
+        """Raw OTA settings register."""
+        return self._word
+
+    @property
+    def cint(self):
+        """Connect integrating C if 1."""
+        return ((self.word >> self.CINT_POS) & 0b1)
+
+    @cint.setter
+    def cint(self, value):
+        if value:
+            v = 1
+        else:
+            v = 0
+        self.word[self.CINT_POS] = v
+
+    @property
+    def zero(self):
+        """Short C to Vcm if 1."""
+        return ((self.word >> self.ZERO_POS) & 0b1)
+
+    @zero.setter
+    def zero(self, value):
+        if value:
+            v = 1
+        else:
+            v = 0
+        self._word[self.ZERO_POS] = v
+
+    @property
+    def se(self):
+        """Set amplifier to single-ended mode if 1, else differential
+        input mode."""
+        return ((self.word >> self.SE_POS) & 0b1)
+
+    @se.setter
+    def se(self, value):
+        if value:
+            v = 1
+        else:
+            v = 0
+        self._word[self.SE_POS] = v
+
+    @property
+    def fast(self):
+        """Increase Gm if 1."""
+        return ((self.word >> self.FAST_POS) & 0b1)
+
+    @fast.setter
+    def fast(self, value):
+        if value:
+            v = 1
+        else:
+            v = 0
+        self._word[self.FAST_POS] = v
+
+    @property
+    def tune(self):
+        """Raw OTA tuning word as gain:4, offset:8.  Use .gain and .offset
+        instead."""
+        return self.word[self.TUNE_WIDTH:]
+
+    @tune.setter
+    def tune(self, value):
+        v = intbv(value, max=2**self.TUNE_WIDTH)
+        self._word[self.TUNE_WIDTH:] = v
+
+    @property
+    def gain(self):
+        """4-bit unsigned bias current setting."""
+        b = self.GAIN_SHIFT
+        a = b + self.GAIN_WIDTH
+        return self.word[a:b]
+
+    @gain.setter
+    def gain(self, value):
+        v = intbv(value, max=2**self.GAIN_WIDTH)
+        b = self.GAIN_SHIFT
+        a = b + self.GAIN_WIDTH
+        self._word[a:b] = v
+
+    @property
+    def offset(self):
+        """8-bit signed bias current offset value.  Stored as offset binary."""
+        b = self.OFFSET_SHIFT
+        a = b + self.OFFSET_WIDTH
+        v = self._word[a:b]
+        # convert from offset binary to signed intbv
+        v ^= 0x80
+        v = intbv(v, max=2**self.OFFSET_WIDTH).signed()
+        ret = intbv(v,
+                min=-2**(self.OFFSET_WIDTH-1),
+                max=2**((self.OFFSET_WIDTH-1)-1))
+        return ret
+
+    @offset.setter
+    def offset(self, value):
+        v = intbv(value,
+                min=-2**(self.OFFSET_WIDTH-1),
+                max=2**((self.OFFSET_WIDTH-1)-1))
+        #convert to offset binary
+        b = self.OFFSET_SHIFT
+        a = b + self.OFFSET_WIDTH
+        v = (v ^ (1 << (self.OFFSET_WIDTH-1)))[self.OFFSET_WIDTH:]
+        self._word[a:b] = v
+
+
+class Harmonic(object):
+    HARMONIC_WIDTH = 48
+    CAL_POS = 47
+    NCO_SHIFT = 32
+    OTA_A_SHIFT = 16
+    OTA_B_SHIFT = 0
+
+    def __init__(self, nco=None, ota=None):
+        self._word = intbv(0)[self.HARMONIC_WIDTH:]
+        self._cal = intbv(0)[0]
+        self.nco = NCO()
+        self.otaA = OTA()
+        self.otaB = OTA()
+        self.ota = (self.otaA, self.otaB)
+
+    @property
+    def cal(self):
+        """Set calibration mode for both OTAs if 1."""
+        return self._cal
+
+    @cal.setter
+    def cal(self, value):
+        if value:
+            v = 1
+        else:
+            v = 0
+        self._cal = intbv(v, max=1)
+
+    @property
+    def word(self):
+        """Construct and return the composite control word."""
+        n = self.nco.word
+        oa = self.otaA.word
+        ob = self.otaB.word
+        self._word = intbv(0)[48:]
+        self._word += (self.cal << self.CAL_POS)
+        self._word += (n << self.NCO_SHIFT)
+        self._word += (oa << self.OTA_A_SHIFT)
+        self._word += (ob << self.OTA_B_SHIFT)
+        return self._word
+
+    @property
+    def bytes(self):
+        """Return the control data as a byte sequence in MSB..LSB order."""
+        w = self.word
+        b = [w[i:i-8] for i in range(self.HARMONIC_WIDTH, 0, -8)]
+        return tuple(b)
+
+
+class Chain(object):
+    def __init__(self, length=48):
+        self.length = length
+        h = [Harmonic() for i in range(self.length)]
+        self.h = tuple(h)
+
+    @property
+    def bytes(self):
+        """Return the control data as a byte sequence in MSB..LSB order."""
+        data = []
+        # TODO: check ordering WRT Chain0Ctl mux
+        for h in self.h:
+            data.extend(h.bytes)
+        return data
+
+
+class Mux(object):
+    def __init__(self):
+        self.sel = 0
+
+
+o = OTA()
+o.tune = 0x3ff
+print o.tune
+print o.word
+o.word[15] = 1
+print o.word
+
+print o.cint
+o.cint = 0
+print o.cint
+o.cint = 1
+print o.cint
+
+
+h = Harmonic()
+h.otaA.offset = -1
+print 'offset:', h.otaA.offset
+o = h.otaA.offset
+
+
+def arr(a):
+    return array('B', a)
+
+
+
+
+if 0:
+    sc0 = SpiController()
+    sc0.configure(0x0403, 0x6011, 0, loopback=True)
+
+    sc1 = SpiController()
+    sc1.configure(0x0403, 0x6011, 1, loopback=True)
+
+    p0 = sc0.get_port(0)
+    p1 = sc1.get_port(0)
+
+    def rw(p, a, readlen=None):
+        if readlen is None:
+            readlen = len(a)
+        cmd = array('B')
+        cmd.fromstring(struct.pack('<BH', Ftdi.RW_BYTES_NVE_PVE_MSB, len(a)-1))
+        cmd.extend(a)
+        cmd.extend(p._controller._immediate)
+        p._controller._ftdi.write_data(cmd)
+        return p._controller._ftdi.read_data_bytes(readlen, 4)
+
+
+    print rw(p0, arr([0,1,2,3]))
+    print rw(p1, arr([4,5,6,7]))