From: Dan White Date: Sun, 8 Apr 2012 20:32:29 +0000 (-0500) Subject: Add library for talking to AtoI devboard X-Git-Tag: calibrations~118 X-Git-Url: http://git.whiteaudio.com/gitweb/?a=commitdiff_plain;h=fd9c19108397caaa1f2f38aa41efc8da95011fd6;p=430.git Add library for talking to AtoI devboard --- diff --git a/python-lib/usbio.py b/python-lib/usbio.py new file mode 100644 index 0000000..b0ee15a --- /dev/null +++ b/python-lib/usbio.py @@ -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('