Update devboard libraries
authorDan White <dan@whiteaudio.com>
Mon, 30 Apr 2012 19:10:55 +0000 (14:10 -0500)
committerDan White <dan@whiteaudio.com>
Mon, 30 Apr 2012 19:10:55 +0000 (14:10 -0500)
python-lib/mpsse-test.py
python-lib/usbio.py

index c1502d9247f9dca09b54b9095cf634e44eb429fd..380de5875e42cd8648c9231076fee7af1fbb9cc4 100755 (executable)
@@ -6,9 +6,7 @@ from time import sleep
 from myhdl import intbv
 from myhdl import bin as b
 
-import mpsse
 import usbio
-from usbio import int2str
 
 import IPython
 import numpy as np
@@ -22,326 +20,142 @@ def bytes2str(self, c):
             raise TypeError, 'Cannot convert type %s to raw string.' % type(c)
     return r
 
+def mkcfg(n):
+    return ''.join(map(chr, np.random.randint(0, 256, n)))
 
-class AtoiSPI0(mpsse.MPSSE):
-    def __init__(self, freq):
-        super(AtoiSPI0, self).__init__()
-        self.Open(0x0403, 0x6011, mpsse.SPI1, int(freq),
-                interface=mpsse.IFACE_A)
-        self.SetCS('flash')
-
-    def SetCS(self, name):
-        """Setup the chip-select pins for a given 'name'."""
-        if name == 'flash':
-            self.context.pidle = self.context.pstop = 0xc8
-            self.context.pstart = 0xc0
-        elif name == 'dac':
-            self.context.pidle = self.context.pstop = 0xc8
-            self.context.pstart = 0xd8
-        elif name == 'adc':
-            self.context.pidle = self.context.pstop = 0xc8
-            self.context.pstart = 0xe8
-        elif name == 'convst':
-            self.context.pidle = self.context.pstop = 0xc8
-            self.context.pstart = 0xf8
-        else:
-            raise Exception, 'Unknown CS name: %s' % name
-
-
-
-class AtoiSPI1(mpsse.MPSSE):
-    def __init__(self, freq):
-        super(AtoiSPI1, self).__init__()
-        self.Open(0x0403, 0x6011, mpsse.SPI0, int(freq),
-                interface=mpsse.IFACE_B)
-        self.SetCS('chain0_conf')
-
-    def SetCS(self, name):
-        """Setup the chip-select pins for a given 'name'."""
-        if name == 'chain0_conf':
-            self.context.pidle = self.context.pstop = 0xf8
-            self.context.pstart = 0xf0
-        elif name == 'chain0_mux':
-            self.context.pidle = self.context.pstop = 0xf8
-            self.context.pstart = 0xe8
-        elif name == 'chain1_conf':
-            self.context.pidle = self.context.pstop = 0xf8
-            self.context.pstart = 0xd0
-        elif name == 'chain1_mux':
-            self.context.pidle = self.context.pstop = 0xf8
-            self.context.pstart = 0xb8
-        else:
-            raise Exception, 'Unknown CS name: %s' % name
-
-
-
-class AtoiI2C(mpsse.MPSSE):
-    def __init__(self, freq):
-        super(AtoiI2C, self).__init__()
-        self.Open(0x0403, 0x6011, mpsse.I2C, int(freq),
-                interface=mpsse.IFACE_A)
-        
-    def _tostring(self, c):
-        r = c
-        if not isinstance(c, str):
-            if isinstance(c, (tuple, list)):
-                r = r''.join(map(chr, c))
-            else:
-                raise TypeError, 'Cannot convert type %s to raw string.' % type(c)
-        return r
-
-    def write(self, c):
-        outdata = self._tostring(c)
-
-        n = 0
-        i = len(outdata)-1
-        s = []
-        for d in outdata:
-            r = struct.unpack('>B', d)[0]
-            s.append(b(r, 8))
-            n += r << (i*8)
-            i -= 1
-        #print ' '.join(s)
-
-
-        self.Start()
-        self.Write(outdata)
-        self.Stop()
-
-    def read(self, c, n):
-        outdata = self._tostring(c)
-        self.Start()
-        self.Write(outdata)
-        r = self.Read(n)
-        self.Stop()
-        return r
-
-
-#
-# Atoi UART / rs232 is on IFACE_D
-#
-
-
-
-
-class DigiReg(usbio.AD524x):
-    """
-    Wrapper for digital potentiometers which control power supply voltages.
-    Allows setting desired voltage instead of pot position.
-
-    These power supplies have a regulator output of:
-        Vout = Vref * (1 + (Ra + Rpot)/Rb)
-    It is sufficient then to specify the end voltages at pos=0 and pos=255 as
-    the output is linear with pot position.
-    """
-    def __init__(self, i2cbus, addr, va=[0.0,1.0], vb=[0.0, 1.0]):
-        super(DigiReg, self).__init__(i2cbus, addr)
-        # calibration voltages, measure actual hardware
-        self.va_min = min(va)
-        self.va_max = max(va)
-        self.vb_min = min(vb)
-        self.vb_max = max(vb)
-        # POR pot position
-        self.posA = 128
-        self.posB = 128
-
-    def _va_getter(self):
-        return ((float(self.posA) / 255) * (self.va_max - self.va_min)
-                + self.va_min)
-
-    def _va_setter(self, value):
-        v = max(value, self.va_min)
-        v = min(v, self.va_max)
-        n = int(round(255 * (v - self.va_min) / (self.va_max - self.va_min)))
-        self.posA = n
-
-    va = property(_va_getter, _va_setter)
-
-    def _vb_getter(self):
-        return ((float(self.posB) / 255) * (self.vb_max - self.vb_min)
-                + self.vb_min)
-
-    def _vb_setter(self, value):
-        v = max(value, self.vb_min)
-        v = min(v, self.vb_max)
-        n = int(round(255 * (v - self.vb_min) / (self.vb_max - self.vb_min)))
-        self.posB = n
-
-    vb = property(_vb_getter, _vb_setter)
-
-    def alias(self, source, dest):
-        """
-        Make a property named by string dest which is an alias for actual
-        property string source.  The valid sources are 'va' and 'vb'.
-
-        > self.alias('va', 'vfoo')
-        > self.vfoo = 1.2
-        > self.va = 1.2 #same effect as above
-        """
-        if source == 'va':
-            setattr(DigiReg, dest,
-                    property(lambda s: s.va, lambda s,v: s._va_setter(v)))
-            setattr(DigiReg, dest+'_min', self.va_min)
-            setattr(DigiReg, dest+'_max', self.va_max)
-        elif source == 'vb':
-            setattr(DigiReg, dest,
-                    property(lambda s: s.vb, lambda s,v: s._vb_setter(v)))
-            setattr(DigiReg, dest+'_min', self.vb_min)
-            setattr(DigiReg, dest+'_max', self.vb_max)
-
-
-
-
-
-def dacval(value):
-    v = intbv(0)[32:]
-    v[28:24] = 0x3
-    v[24:20] = 0xf
-    v += (value << 4)
-    return int2str(v, 32)
-
+def cfg2hexstr(x):
+    return ''.join(map(lambda h: '%02x' % ord(h), x))
 
 
-spi0 = AtoiSPI0(100e3)
-i2c = AtoiI2C(10e3)
 
-def w(v):
-    spi0.Start()
-    spi0.Write(v)
-    spi0.Stop()
+##############################################################################
+# Setup FTDI serial ports
+#
+spi0 = usbio.AtoiSPI0(1000e3) #port A
+spi1 = usbio.AtoiSPI1(500e3)  #port B
+i2c = usbio.I2C(interface=usbio.ftdi.INTERFACE_C, scl=0, sda=1,
+        vid=0x0403, pid=0x6011, delay=10e-6)
 
 
+##############################################################################
+# Analog bias
+# (bias generator also uses DAC_1 and DAC_3,
+#  need to make a wrapper class to do ibias.buf = 10 uA)
+#
 ibias = usbio.AD524x(i2c, 0)
+ibias.posA = 0
+ibias.posB = 0
 
-vatoi = DigiReg(i2c, 2, (0.5011, 1.2972), (1.9544, 2.7503))
+
+##############################################################################
+# Power supplies
+#
+vatoi = usbio.DigiReg(i2c, 2, (0.5011, 1.2972), (1.9544, 2.7503))
 vatoi.alias('va', 'vdd')
 vatoi.alias('vb', 'avdd')
 
-v430 = DigiReg(i2c, 3, (1.9539, 2.7325), (0.5006, 1.2776))
+v430 = usbio.DigiReg(i2c, 3, (1.9539, 2.7325), (0.5006, 1.2776))
 v430.alias('va', 'dvdd')
 v430.alias('vb', 'vdd')
 
-
-#
-# Power supply testing
-#
-if 0:
-    v430.posA = 0
-    v430.update(True)
-    sleep(1)
-    v430.posA = 255
-    v430.update(True)
-    sleep(1)
-    v430.posB = 0
-    v430.update(True)
-    sleep(1)
-    v430.posB = 255
-    v430.update(True)
-
-
-
-#
-# DAC testing
-#
-if 0:
-    #turn on internal reference always
-    spi0.SetCS('dac')
-
-    int_ref_always_on = intbv(0)[32:]
-    int_ref_always_on[27] = 1
-    int_ref_always_on[24] = 1
-    int_ref_always_on[19] = 1
-    int_ref_always_on[13] = 1
-    w(int2str(int_ref_always_on, 32))
-
-    all_dacs_on = intbv(0)[32:]
-    all_dacs_on[26] = 1
-    all_dacs_on[8:] = 0xff
-    w(int2str(all_dacs_on, 32))
-
-    w(dacval(0x8000))
-
-
-
-
-#
-# ADC testing
-#
-if 0:
-    adc = usbio.ADS8201(spi0)
-    adc.setRegister(adc.TRIGGER_SCR, 0x02)
-    rv  = adc.getRegister(adc.TRIGGER_SCR)
-    print 'receivd:', b(rv, 16)
-
-    def sr(n):
-        adc.setRegister(adc.CHAN_SEL, n)
-        rv = adc.getRegister(adc.CHAN_SEL)
-        print 'receivd:', b(rv, 16)
-
-
-
-
-#
-# devboard setup
-#
-# power supply defaults
+# setup power supply defaults
 def psdefaults():
-    sleep(1)
     print "Setting atoi.avdd"
     vatoi.avdd = 2.5
-    sleep(1)
     print "Setting atoi.vdd"
     vatoi.vdd = 1.2
-    sleep(1)
     print "Setting 430.vdd"
     v430.vdd = 1.2
-    sleep(1)
     print "Setting 430.dvdd"
     v430.dvdd = 2.5
 
+psdefaults()
 
-#psdefaults()
 
+##############################################################################
+# DAC
+#
+dac = usbio.DAC_atoi(spi0, 'dac')
+dac.swreset()
+
+# reference is tied to ADC also
+dac.reference(dac.REF_FLEXIBLE_ALWAYS_ON)
+
+# CLR is tied high, explicitly ignore anyway
+dac.CCR(dac.IGNORE_CLR)
+
+# power up used channels
+bitfield = 0
+for i in (0, 1, 2, 3, 4):
+    bitfield += (1 << i)
+dac.power(dac.POWER_ON, bitfield)
+
+# power up used channels
+bitfield = 0
+for i in (0, 1, 2, 3, 4):
+    bitfield += (1 << i)
+dac.power(dac.POWER_ON, bitfield)
+
+# others set 1k to gnd
+bitfield = 0
+for i in (5, 6, 7):
+    bitfield += (1 << i)
+dac.power(dac.POWER_OFF_1k, bitfield)
+
+# set initial output voltages
+# before tuning
+dac.vina(1.0)
+dac.vinb(1.0)
+dac.vcmi(1.0)
+dac.vbias_core(0.0)
+dac.vbias_buf(0.0)
+
+
+##############################################################################
+# ADC
+# (TODO)
+#
+adc = usbio.ADS8201(spi0)
+adc.setRegister(adc.TRIGGER_SCR, 0x02)
+rv  = adc.getRegister(adc.TRIGGER_SCR)
+print 'receivd:', b(rv, 16)
+
+def sr(n):
+    adc.setRegister(adc.CHAN_SEL, n)
+    rv = adc.getRegister(adc.CHAN_SEL)
+    print 'receivd:', b(rv, 16)
 
 
-
-def mkcfg(n):
-    return ''.join(map(chr, np.random.randint(0, 256, n)))
-
-def cfg2hexstr(x):
-    return ''.join(map(lambda h: '%02x' % ord(h), x))
-
-
-#
+##############################################################################
 # chain0 config registers
 #
-spi1 = AtoiSPI1(100e3)
-chain = usbio.Chain(spi1, 'chain0_conf', length=48)
-mux = usbio.Mux(spi1, 'chain0_mux')
-
-#calibration mode for all channels
-for h in chain.h:
-    h.cal = 1
-    for ota in h.ota:
-        ota.se = 0
-        ota.cint = 1
-        ota.zero = 0
-        ota.fast = 1
-        ota.gain = 8
-        ota.offset = 0
-
-cfg = ''.join(map(chr, chain.bytes))
-
-spi1.SetCS('chain0_conf')
-out = spi1.Exchange(cfg)
-print
-print ''.join(map(lambda x: '%02x' % ord(x), cfg))
-print
-print ''.join(map(lambda x: '%02x' % ord(x), out))
-
-out = spi1.Exchange(cfg)
-print
-print ''.join(map(lambda x: '%02x' % ord(x), out))
+if 1:
+    chain = usbio.Chain(spi1, 'chain0_conf', length=48)
+    mux = usbio.Mux(spi1, 'chain0_mux')
+
+    #calibration mode for all channels
+    for h in chain.h:
+        h.cal = 1
+        for ota in h.ota:
+            ota.se = 0
+            ota.cint = 1
+            ota.zero = 0
+            ota.fast = 1
+            ota.gain = 8
+            ota.offset = 0
+
+    cfg = ''.join(map(chr, chain.bytes))
+
+    spi1.SetCS('chain0_conf')
+    out = spi1.Exchange(cfg)
+    print
+    print ''.join(map(lambda x: '%02x' % ord(x), cfg))
+    print
+    print ''.join(map(lambda x: '%02x' % ord(x), out))
+
+    out = spi1.Exchange(cfg)
+    print
+    print ''.join(map(lambda x: '%02x' % ord(x), out))
 
 IPython.embed()
 
index 19ba75a6a837d284212418f76af1282bdf4460f4..367f9c8fa6ceeb0af177d9f3c826a09e749a33ac 100644 (file)
@@ -1,8 +1,10 @@
 #!/usr/bin/env python
 
-from array import array
-import struct
-import time
+
+from time import sleep
+
+import ftdi
+import mpsse
 
 from myhdl import intbv
 from myhdl import bin as b
@@ -60,6 +62,170 @@ def int2str(value, width):
     return ''.join(map(chr, b))
 
 
+class I2C(object):
+    def __init__(self, interface=ftdi.INTERFACE_A, scl=0, sda=1,
+                    vid=0x0403, pid=0x6011, delay=0.001, timeout=100):
+        """Bitbanged I2C for use on FT4232H ports C and D.
+
+        interface = [0,1,2,3]
+        scl/sda = port pin numbers
+        """
+        self.interface = interface
+        self.scl = scl
+        self.sda = sda
+        self.vid = vid
+        self.pid = pid
+        self.DELAY = delay
+        self.TIMEOUT = timeout
+
+        self.context = ftdi.new()
+        ftdi.set_interface(self.context, interface)
+        ftdi.write_data_set_chunksize(self.context, 1)
+        ftdi.read_data_set_chunksize(self.context, 1)
+
+        self.io = intbv(0xff)[8:]
+        self.io[scl] = 1
+        self.io[sda] = 1
+        self.port = intbv(0)[8:]
+
+        ftdi.set_bitmode(self.context, int(self.io), ftdi.BITMODE_BITBANG)
+
+        e = ftdi.usb_open(self.context, vid, pid)
+        if e != 0:
+            raise Exception, 'Could not open usb device, error: %i' % e
+
+        ftdi.set_baudrate(self.context, 1000)
+        ftdi.usb_purge_buffers(self.context)
+        self.started = False
+
+    def _clear_scl(self):
+        self.io[self.scl] = 1
+        ftdi.set_bitmode(self.context, int(self.io), ftdi.BITMODE_BITBANG)
+        self.port[self.scl] = 0
+        ftdi.write_data(self.context, chr(self.port), 1)
+
+    def _read_scl(self):
+        if 1:
+            self.io[self.scl] = 0
+            ftdi.set_bitmode(self.context, int(self.io), ftdi.BITMODE_BITBANG)
+            port = ftdi.read_pins(self.context)
+            scl = intbv(ord(port[1]))[self.scl]
+            if scl:
+                return 1
+            else:
+                return 0
+
+    def _clear_sda(self):
+        self.io[self.sda] = 1
+        ftdi.set_bitmode(self.context, int(self.io), ftdi.BITMODE_BITBANG)
+        self.port[self.sda] = 0
+        ftdi.write_data(self.context, chr(self.port), 1)
+
+    def _read_sda(self):
+        if 1:
+            self.io[self.sda] = 0
+            ftdi.set_bitmode(self.context, int(self.io), ftdi.BITMODE_BITBANG)
+            port = ftdi.read_pins(self.context)
+            sda = intbv(ord(port[1]))[self.sda]
+            if sda:
+                return 1
+            else:
+                return 0
+
+    def _clock_stretch(self):
+        i = 0
+        while (self._read_scl() == 0):
+            sleep(self.DELAY)
+            i += 1
+            if i > self.TIMEOUT:
+                raise Exception, "Timeout waiting for SCL to go high."
+
+    def _start(self):
+        if self.started:
+            self._read_sda()
+            sleep(self.DELAY)
+            self._clock_stretch()
+            sleep(self.DELAY)
+        if self._read_sda() == 0:
+            raise Exception, "Lost bus arbitration."
+        self._clear_sda()
+        sleep(self.DELAY)
+        self._clear_scl()
+        self.started = True
+
+    def _stop(self):
+        self._clear_sda()
+        sleep(self.DELAY)
+
+        self._clock_stretch()
+
+        sleep(self.DELAY)
+        if self._read_sda() == 0:
+            raise Exception, "Lost bus arbitration."
+        sleep(self.DELAY)
+        self.started = False
+
+    def _write_bit(self, bit):
+        if bit:
+            self._read_sda()
+        else:
+            self._clear_sda()
+        sleep(self.DELAY)
+        self._clock_stretch()
+        if bit and (self._read_sda() == 0):
+            raise Exception, "Lost bus arbitration."
+        sleep(self.DELAY)
+        self._clear_scl()
+
+    def _read_bit(self):
+        self._read_sda()
+        sleep(self.DELAY)
+        self._clock_stretch()
+        bit = self._read_sda()
+        sleep(self.DELAY)
+        self._clear_scl()
+        return bit
+
+    def _write_byte(self, byte, start, stop):
+        byt = intbv(byte, max=256)
+        if start:
+            self._start()
+        for i in range(7, -1, -1):
+            bit = byt[i]
+            self._write_bit(bit)
+        nack = self._read_bit()
+        if stop:
+            self._stop()
+        return nack
+
+    def _read_byte(self, nack, stop):
+        byte = 0
+        for i in range(8):
+            byte = (byte << 1) | self._read_bit()
+        self._write_bit(nack)
+        if stop:
+            self._stop()
+        return byte
+
+    def write(self, data, stop=True):
+        """Send i2c command data to slave,
+        data is a list-like of integers constrained to uint8 range."""
+        n = len(data)
+        self._write_byte(data[0], start=True, stop=False)
+        for i in range(1, n-1):
+            self._write_byte(data[i], start=False, stop=False)
+        self._write_byte(data[n-1], start=False, stop=stop)
+
+    def read(self, n):
+        """Read n bytes from a slave.  Usually preceded by write(data, stop=False).
+        """
+        data = []
+        for i in range(n-1):
+            data.append(self._read_byte(0, stop=False))
+        data.append(self._read_byte(0, stop=True))
+        return data
+
+
 class NCO(object):
     RST_POS = 14
     FCW_WIDTH = 14
@@ -379,7 +545,6 @@ class Mux(object):
         return out
 
 
-
 class AD524x(object):
     ADDR_BASE = intbv(0b0101100, max=2**7)
     SEL_POS = 7
@@ -516,6 +681,7 @@ class AD524x(object):
                self.instruction,
                pos)
 
+        #print ' '.join(map(lambda v: b(v, 8), cmd))
         self.lastSel = self.sel
         if sel:
             self.lastB = pos
@@ -537,13 +703,11 @@ class AD524x(object):
             self.send(sel)
 
         cmd = ((self.addr << 1) + self.READ, )
-        val = self.bus.read(cmd, 1)
+        self.bus.write([cmd], stop=False)
+        val = self.bus.read(1)
         return val[0]
 
 
-
-
-
 class DAC8568(object):
     CTL_WIDTH = 32
     DAC_WIDTH = 16
@@ -581,6 +745,7 @@ class DAC8568(object):
         self.bus = spibus
         self.cs = 'dac'
         self._word = intbv(0)[self.CTL_WIDTH:]
+        self._chpos = [0 for i in range(8)]
 
     def __str__(self):
         return ''.join(map(chr, self.bytes()))
@@ -593,31 +758,38 @@ class DAC8568(object):
         self._clear()
         self._control(0x5)
         self._feature(mode)
+        self.send()
 
     def LDAC(self, bitfield):
         self._clear()
         self._control(0x6)
         self._word[8:0] = intbv(bitfield, max=2**8)
+        self.send()
 
     def swreset(self):
         self._clear()
         self._control(0x7)
+        self.send()
 
-    def set(self, addr, value, mode):
+    def set(self, addr, value, mode=INPUT_UPDATE_SINGLE):
         self._clear()
         self._control(mode)
         self._address(addr)
         self._data(value)
+        self._chpos[addr] = value
+        self.send()
 
     def power(self, mode, bitfield):
         self._clear()
         self._control(0x4)
         self._word[10:8] = intbv(mode, max=2**2)
         self._word[8:0] = intbv(bitfield, max=2**8)
+        self.send()
 
     def reference(self, mode):
         self._clear()
         self._word = intbv(mode, max=2**32)
+        self.send()
 
     def bytes(self):
         """Return the control data as a byte sequence in MSB..LSB order."""
@@ -646,15 +818,12 @@ class DAC8568(object):
             self._word[24:20] = intbv(value, max=2**4)
 
     def _data(self, value):
-        self._word[20:4] = intbv(value, max=2**16)
+        self._word[20:20-self.DAC_WIDTH] = intbv(value, max=2**self.DAC_WIDTH)
 
     def _feature(self, value):
         self._word[4:0] = intbv(value, max=2**4)
         
 
-
-
-
 class ADS8201(object):
     CH01_CCR = 0
     CH23_CCR = 1
@@ -675,21 +844,21 @@ class ADS8201(object):
         w = intbv(0x8000)[16:]
         w[14:10] = intbv(address, max=2**4)
         w[8:] = intbv(value, max=2**8)
-        print 'sending:', b(w, 16)
+        #print 'sending:', b(w, 16)
         self.bus.SetCS('adc')
         self.bus.Start()
         self.bus.Write(int2str(w, 16))
-        time.sleep(1e-3)
+        sleep(1e-3)
         self.bus.Stop()
 
     def getRegister(self, address):
         w = intbv(0x4000)[16:]
         w[14:10] = intbv(address, max=2**4)
-        print 'sending:', b(w, 16)
+        #print 'sending:', b(w, 16)
         self.bus.SetCS('adc')
         self.bus.Start()
         rval = self.bus.Exchange(int2str(w, 16))
-        time.sleep(1e-3)
+        sleep(1e-3)
         self.bus.Stop()
         i = 8 * (len(rval)-1)
         r = 0
@@ -712,68 +881,175 @@ class ADS8201(object):
         return r
 
 
+class AtoiSPI0(mpsse.MPSSE):
+    def __init__(self, freq):
+        super(AtoiSPI0, self).__init__()
+        self.Open(0x0403, 0x6011, mpsse.SPI1, int(freq),
+                interface=mpsse.IFACE_A)
+        self.SetCS('flash')
+
+    def SetCS(self, name):
+        """Setup the chip-select pins for a given 'name'."""
+        if name == 'flash':
+            self.context.pidle = self.context.pstop = 0xc8
+            self.context.pstart = 0xc0
+        elif name == 'dac':
+            self.context.pidle = self.context.pstop = 0xc8
+            self.context.pstart = 0xd8
+        elif name == 'adc':
+            self.context.pidle = self.context.pstop = 0xc8
+            self.context.pstart = 0xe8
+        elif name == 'convst':
+            self.context.pidle = self.context.pstop = 0xc8
+            self.context.pstart = 0xf8
+        else:
+            raise Exception, 'Unknown CS name: %s' % name
+
+
+class AtoiSPI1(mpsse.MPSSE):
+    def __init__(self, freq):
+        super(AtoiSPI1, self).__init__()
+        self.Open(0x0403, 0x6011, mpsse.SPI0, int(freq),
+                interface=mpsse.IFACE_B)
+        self.SetCS('chain0_conf')
+
+    def SetCS(self, name):
+        """Setup the chip-select pins for a given 'name'."""
+        if name == 'chain0_conf':
+            self.context.pidle = self.context.pstop = 0xf8
+            self.context.pstart = 0xf0
+        elif name == 'chain0_mux':
+            self.context.pidle = self.context.pstop = 0xf8
+            self.context.pstart = 0xe8
+        elif name == 'chain1_conf':
+            self.context.pidle = self.context.pstop = 0xf8
+            self.context.pstart = 0xd0
+        elif name == 'chain1_mux':
+            self.context.pidle = self.context.pstop = 0xf8
+            self.context.pstart = 0xb8
+        else:
+            raise Exception, 'Unknown CS name: %s' % name
+
 
-class I2C(object):
-    def write(self, data):
-        """Send i2c command data to slave."""
-        raise NotImplementedError
 
-    def read(self, cmd, n):
-        """Send i2c command cmd and read n additional bytes from slave."""
-        raise NotImplementedError
 
+#
+# Atoi UART / rs232 is on IFACE_D
+#
 
 
-if __name__ == '__main__':
+class DigiReg(AD524x):
+    """
+    Wrapper for digital potentiometers which control power supply voltages.
+    Allows setting desired voltage instead of pot position.
+
+    These power supplies have a regulator output of:
+        Vout = Vref * (1 + (Ra + Rpot)/Rb)
+    It is sufficient then to specify the end voltages at pos=0 and pos=255 as
+    the output is linear with pot position.
+    """
+    def __init__(self, i2cbus, addr, va=[0.0,1.0], vb=[0.0, 1.0]):
+        super(DigiReg, self).__init__(i2cbus, addr)
+        # calibration voltages, measure actual hardware
+        self.va_min = min(va)
+        self.va_max = max(va)
+        self.vb_min = min(vb)
+        self.vb_max = max(vb)
+        # POR pot position
+        self.posA = 128
+        self.posB = 128
+
+    def _va_getter(self):
+        return ((float(self.posA) / 255) * (self.va_max - self.va_min)
+                + self.va_min)
+
+    def _va_setter(self, value):
+        v = max(value, self.va_min)
+        v = min(v, self.va_max)
+        n = int(round(255 * (v - self.va_min) / (self.va_max - self.va_min)))
+        self.posA = n
+
+    va = property(_va_getter, _va_setter)
+
+    def _vb_getter(self):
+        return ((float(self.posB) / 255) * (self.vb_max - self.vb_min)
+                + self.vb_min)
+
+    def _vb_setter(self, value):
+        v = max(value, self.vb_min)
+        v = min(v, self.vb_max)
+        n = int(round(255 * (v - self.vb_min) / (self.vb_max - self.vb_min)))
+        self.posB = n
+
+    vb = property(_vb_getter, _vb_setter)
+
+    def alias(self, source, dest):
+        """
+        Make a property named by string dest which is an alias for actual
+        property string source.  The valid sources are 'va' and 'vb'.
+
+        > self.alias('va', 'vfoo')
+        > self.vfoo = 1.2
+        > self.va = 1.2 #same effect as above
+        """
+        if source == 'va':
+            setattr(DigiReg, dest,
+                    property(lambda s: s.va, lambda s,v: s._va_setter(v)))
+            setattr(DigiReg, dest+'_min', self.va_min)
+            setattr(DigiReg, dest+'_max', self.va_max)
+        elif source == 'vb':
+            setattr(DigiReg, dest,
+                    property(lambda s: s.vb, lambda s,v: s._vb_setter(v)))
+            setattr(DigiReg, dest+'_min', self.vb_min)
+            setattr(DigiReg, dest+'_max', self.vb_max)
+
+
+class DAC_atoi(DAC8568):
+    """Specific configuration and calibration for devboard DAC."""
+    VREF = 2.5
+    GAIN = 2.0 #part options C/D
+    #POR_VALUE = 0 #part C
+    POR_VALUE = DAC8568.DAC_WIDTH / 2 #part D
+
+    #zero-code output voltages
+    VOS = ( 0.0,
+            0.0,
+            0.0,
+            5e-3,
+            0.0,
+            0.0,
+            0.0,
+            0.0)
 
-    if 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
+    def __init__(self, spibus, cs='dac'):
+        super(DAC_atoi, self).__init__(spibus, cs)
+        self._chpos = [self.POR_VALUE for i in range(8)]
+
+    def _pos2v(self, pos):
+        return (pos / float(2**self.DAC_WIDTH)) * self.VREF * self.GAIN
+
+    def setv(self, channel, voltage, mode=DAC8568.INPUT_UPDATE_SINGLE):
+        code = voltage * float(2**self.DAC_WIDTH) / self.VREF / self.GAIN
+        code = int(round(code))
+        code = min(2**self.DAC_WIDTH - 1, code)
+        code = max(0, code)
+        self.set(channel, code, mode)
+
+    def vina(self, v, mode=DAC8568.INPUT_UPDATE_SINGLE):
+        self.setv(0, v, mode)
+
+    def vinb(self, v, mode=DAC8568.INPUT_UPDATE_SINGLE):
+        self.setv(2, v, mode)
+
+    def vcmi(self, v, mode=DAC8568.INPUT_UPDATE_SINGLE):
+        self.setv(4, v, mode)
+
+    def vbias_core(self, v, mode=DAC8568.INPUT_UPDATE_SINGLE):
+        self.setv(1, v, mode)
+
+    def vbias_buf(self, v, mode=DAC8568.INPUT_UPDATE_SINGLE):
+        self.setv(3, v, mode)
 
-        h = Harmonic()
-        h.otaA.offset = -1
-        print 'offset:', h.otaA.offset
-        o = h.otaA.offset
 
-        def arr(a):
-            return array('B', a)
 
-        if 1:
-            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 'Loopback SPI interfaces 0, 1'
-            a = [0,1,2,3,4,5,6,7,8,9]
-            print a
-            print rw(p0, arr(a))
-
-            a = [i for i in range(256)]
-            print a
-            print rw(p1, arr(a))