--- /dev/null
+#!/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]))