v = intbv(value, max=2**self.FCW_WIDTH)
self._word[self.FCW_WIDTH:] = v
+ def dump_settings(self):
+ """Return a dict appropriate for passing to constructor to restore the
+ amplifier state."""
+ s = {}
+ s['rst'] = int(self.rst)
+ s['fcw'] = int(self.fcw)
+ return s
+
class OTA(object):
CINT_POS = 15
v = (v ^ (1 << (self.OFFSET_WIDTH-1)))[self.OFFSET_WIDTH:]
self._word[a:b] = v
+ def dump_settings(self):
+ """Return a dict appropriate for passing to constructor to restore the
+ amplifier state."""
+ s = {}
+ s['cint'] = int(self.cint)
+ s['zero'] = int(self.zero)
+ s['se'] = int(self.se)
+ s['fast'] = int(self.fast)
+ s['gain'] = int(self.gain)
+ s['offset'] = int(self.offset)
+ return s
+
+
class MuxOTA(OTA):
- """Cast bits cint and zero as the mode. Bit se is unused."""
+ """Cast bits cint and zero as the mode. Bit se is unused.
+ TODO: verify bit positions and shifts -- symbols are 3-bits wide.
+ """
MODE_SHIFT = 13
MODE_WIDTH = 3
CAL_CMP = 4
CAL_BUF = 6
+ def __init__(self, mode=0, fast=0, gain=0, offset=0):
+ # setup main functionality
+ super(MuxOTA, self).__init__(fast=fast, gain=gain, offset=offset)
+ # remove references to re-defined bits, they are named ".mode" now
+ del self.__dict__['cint']
+ del self.__dict__['zero']
+ del self.__dict__['se']
+ self.mode = mode
+
@property
def mode(self):
b = self.MODE_SHIFT
a = b + self.MODE_WIDTH
self._word[a:b] = v
+ def dump_settings(self):
+ """Return a dict appropriate for passing to constructor to restore the
+ amplifier state."""
+ s = {}
+ s['mode'] = int(self.mode)
+ s['fast'] = int(self.fast)
+ s['gain'] = int(self.gain)
+ s['offset'] = int(self.offset)
+ return s
class Harmonic(object):
HARMONIC_WIDTH = 48
OTA_A_SHIFT = 16
OTA_B_SHIFT = 0
- def __init__(self, nco=None, ota=None):
+ def __init__(self, cal=0, nco=None, otaA=None, otaB=None):
+ """Restore state via kwargs, nco, otaA, and otaB are dicts to send to
+ the respective constructors if specified."""
self._word = intbv(0)[self.HARMONIC_WIDTH:]
- self._cal = intbv(0)[0]
- self.nco = NCO()
- self.otaA = OTA()
- self.otaB = OTA()
+
+ self.cal = intbv(cal)[0]
+
+ if nco:
+ self.nco = NCO(**nco)
+ else:
+ self.nco = NCO()
+
+ if otaA:
+ self.otaA = OTA(**otaA)
+ else:
+ self.otaA = OTA()
+
+ if otaB:
+ self.otaB = OTA(**otaB)
+ else:
+ self.otaB = OTA()
+
self.ota = (self.otaA, self.otaB)
@property
b = [w[i:i-8] for i in range(self.HARMONIC_WIDTH, 0, -8)]
return tuple(b)
+ def dump_settings(self):
+ """Return a dict appropriate for passing to constructor to restore the
+ harmonic's state."""
+ s = {}
+ s['cal'] = int(self.cal)
+ s['nco'] = self.nco.dump_settings()
+ s['otaA'] = self.otaA.dump_settings()
+ s['otaB'] = self.otaB.dump_settings()
+ return s
+
class Chain(object):
- def __init__(self, spibus, csname, length=48):
+ def __init__(self, spibus, csname, length=48, harmonics=None):
self.bus = spibus
self.csname = csname
self.length = length
- h = [Harmonic() for i in range(self.length)]
+
+ # restore settings from list of dicts if given
+ if harmonics:
+ h = [Harmonic(**hi) for hi in harmonics]
+ else:
+ h = [Harmonic() for i in range(self.length)]
+
self.h = tuple(h)
def __str__(self):
#self.bus.Stop()
return out
+ def dump_settings(self):
+ """Return a dict appropriate for passing to constructor to restore the
+ chain's state."""
+ s = {}
+ s['length'] = int(self.length)
+ s['harmonics'] = [hi.dump_settings() for hi in self.h]
+ return s
+
class Mux(object):
MUX_WIDTH = 48
OTA_B_SHIFT = 0
- def __init__(self, spibus, csname):
+ def __init__(self, spibus, csname, selA=48, selB=48, otaA=None, otaB=None):
self.bus = spibus
self.csname = csname
self._word = intbv(0)[self.MUX_WIDTH:]
- self._selA = intbv(48, max=2**self.SEL_WIDTH) #select CMI
- self._selB = intbv(48, max=2**self.SEL_WIDTH) #select CMI
- self.otaA = MuxOTA()
- self.otaB = MuxOTA()
+ self._selA = intbv(selA, max=2**self.SEL_WIDTH) #select CMI
+ self._selB = intbv(selB, max=2**self.SEL_WIDTH) #select CMI
+
+ if otaA:
+ self.otaA = MuxOTA(**otaA)
+ else:
+ self.otaA = MuxOTA()
+
+ if otaB:
+ self.otaB = MuxOTA(**otaB)
+ else:
+ self.otaB = MuxOTA()
+
self.otaA.mode = self.otaA.MUX_BUF
self.otaB.mode = self.otaB.MUX_BUF
self.ota = (self.otaA, self.otaB)
#self.bus.Stop()
return out
+ def dump_settings(self):
+ """Return a dict appropriate for passing to constructor to restore the
+ mux's state."""
+ s = {}
+ s['selA'] = int(self.selA)
+ s['selB'] = int(self.selB)
+ s['otaA'] = self.otaA.dump_settings()
+ s['otaB'] = self.otaB.dump_settings()
+ return s
+
class AD524x(object):
ADDR_BASE = intbv(0b0101100, max=2**7)
READ = 1
WRITE = 0
- # cache for lazy updating
- lastA = None
- lastB = None
- lastSel = None
- lastO1 = None
- lastO2 = None
+ # cache for lazy updating, init to POR values
+ lastA = 128
+ lastB = 128
+ lastSel = 0
+ lastO1 = truth(0)
+ lastO2 = truth(0)
- def __init__(self, i2cbus, addr, immediate=True):
+ def __init__(self, i2cbus, addr, immediate=True,
+ posA=128, posB=128, gpo1=0, gpo2=0):
self.bus = i2cbus
self.addr = intbv(self.ADDR_BASE + intbv(addr, max=2**2), max=2**7)
self.immediate = immediate
self._sel = False
self._rs = False
self._sd = False
- self._posA = intbv(0x80, max=2**8)
- self._posB = intbv(0x80, max=2**8)
- self.pos = (self._posA, self._posB)
+
+ # POR values
+ self._posA = intbv(128, max=2**8)
+ self._posB = intbv(128, max=2**8)
self._gpo1 = False
self._gpo2 = False
+
+ self.pos = (self._posA, self._posB)
self.gpo = (self._gpo1, self.gpo2)
+ # restore state if necessary
+ self.posA = posA
+ self.posB = posB
+ self.gpo1 = gpo1
+ self.gpo2 = gpo2
+
@property
def sel(self): return self._sel
v = truth(value)
self._gpo1 = v
if immediate or (immediate is None and self.immediate):
- self.sendGPO()
+ self.updateGPO()
@property
def gpo2(self): return self._gpo2
v = truth(value)
self._gpo2 = v
if immediate or (immediate is None and self.immediate):
- self.sendGPO()
+ self.updateGPO()
@property
def instruction(self):
val = self.bus.read(1)
return val[0]
+ def dump_settings(self):
+ """Return a dict of settings which can be passed to constructor to
+ restore the state."""
+ s = {}
+ s['posA'] = self.posA
+ s['posB'] = self.posB
+ s['gpo1'] = self.gpo1
+ s['gpo2'] = self.gpo2
+ return s
class DAC8568(object):
CTL_WIDTH = 32
self.programPage(a, data[s:e])
+class NullSPI(SPI):
+ def write(*args, **kwargs):
+ pass
+
+ def exchange(*args, **kwargs):
+ pass
+
class AtoiSPI0(SPI):
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.
+
+ POR position for fitted part is posA/B = 128.
"""
- def __init__(self, i2cbus, addr, va=[0.0,1.0], vb=[0.0, 1.0]):
+ def __init__(self, i2cbus, addr,
+ va_range=[0.0,1.0], vb_range=[0.0, 1.0],
+ aliases={}, posA=128, posB=128,
+ gpo1=0, gpo2=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
+ self.va_min = min(va_range)
+ self.va_max = max(va_range)
+ self.vb_min = min(vb_range)
+ self.vb_max = max(vb_range)
+ self.aliases = {}
+ for s,d in aliases.iteritems():
+ self.alias(s, d)
+ self.posA = posA
+ self.posB = posB
def _va_getter(self):
return ((float(self.posA) / 255) * (self.va_max - self.va_min)
> self.vfoo = 1.2
> self.va = 1.2 #same effect as above
"""
+ self.aliases[source] = dest
if source == 'va':
setattr(DigiReg, dest,
property(lambda s: s.va, lambda s,v: s._va_setter(v)))
setattr(DigiReg, dest+'_min', self.vb_min)
setattr(DigiReg, dest+'_max', self.vb_max)
+ def dump_settings(self):
+ """Return a dict of settings which can be passed to constructor to
+ restore the state."""
+ s = super(DigiReg, self).dump_settings()
+ s['va_range'] = (self.va_min, self.va_max)
+ s['vb_range'] = (self.vb_min, self.vb_max)
+ s['aliases'] = self.aliases
+ # saved but not used to restore via __init__()
+ s['va'] = self.va
+ s['vb'] = self.vb
+ return s
+
+
+class BiasGen(AD524x):
+ """
+ Wrapper for the two bias generators the i2c-controlled pots and
+ dac-controled resistor ends.
+
+ TODO: equation ibias_x = f(rpot, vdac)
+
+ POR value of fitted rpot is posA/B = 128
+ """
+ def __init__(self, i2cbus, addr,
+ posA=128, posB=128):
+ super(DigiReg, self).__init__(i2cbus, addr,
+ posA=posA, posB=posB)
+
+ def dump_settings(self):
+ """Return a dict of settings appropriate to send to __init__() to
+ restore the state."""
+ # raw settings of Rpot
+ s = super(BiasGen, self).dump_settings()
+ return s
+
+
class DAC_atoi(DAC8568):
"""Specific configuration and calibration for devboard DAC."""
0.0)
- def __init__(self, spibus, cs='dac'):
+ def __init__(self, spibus, cs='dac',
+ vina=None, vinb=None, vcmi=None,
+ vbias_core=None, vbias_buf=None):
super(DAC_atoi, self).__init__(spibus, cs)
self._chpos = [self.POR_VALUE for i in range(8)]
+ # put into official operating mode
+ #
+ dac.swreset()
+ # reference is tied to ADC, do not power down
+ dac.reference(dac.REF_FLEXIBLE_ALWAYS_ON)
+ # CLR is tied high, explicitly ignore anyway
+ dac.CCR(dac.IGNORE_CLR)
+ # power up all channels
+ bitfield = 0xff
+ dac.power(dac.POWER_ON, bitfield)
+ # set outputs if we are restoring state
+ for name, value in (
+ ('vina', vina),
+ ('vinb', vinb),
+ ('vcmi', vcmi),
+ ('vbias_core', vbias_core),
+ ('vbias_buf', vbias_buf),
+ ):
+ if value:
+ getattr(self, name)(value)
+ # zero unused outputs
+ # part A already POR's to 0 but...
+ for i in range(5, 8):
+ dac.setv(i, 0.0)
+
+
def _pos2v(self, pos):
return (pos / float(2**self.DAC_WIDTH)) * self.VREF * self.GAIN
self.setv(3, v, mode)
return self._pos2v(self._chpos[3])
+ def dump_settings(self):
+ """Return a dict of settings appropriate to send to __init__() to
+ restore the DAC's state."""
+ s = {}
+ s['vina'] = self.vina()
+ s['vinb'] = self.vinb()
+ s['vcmi'] = self.vcmi()
+ s['vbias_core'] = self.vbias_core()
+ s['vbias_buf'] = self.vbias_buf()
+ return s