From: Dan White Date: Mon, 15 Oct 2012 19:49:11 +0000 (-0500) Subject: Settings save/restore helpers UNTESTED X-Git-Tag: bootrom-initial-submission~70 X-Git-Url: http://git.whiteaudio.com/gitweb/?a=commitdiff_plain;h=c70e7241e969379a5d979fec262c8b49a064f688;p=430.git Settings save/restore helpers UNTESTED --- diff --git a/python-lib/usbio.py b/python-lib/usbio.py index d9c0f53..5b807a5 100644 --- a/python-lib/usbio.py +++ b/python-lib/usbio.py @@ -653,6 +653,14 @@ class NCO(object): 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 @@ -774,9 +782,24 @@ class OTA(object): 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 @@ -786,6 +809,15 @@ class MuxOTA(OTA): 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 @@ -799,6 +831,15 @@ class MuxOTA(OTA): 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 @@ -807,12 +848,28 @@ class Harmonic(object): 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 @@ -845,13 +902,29 @@ class Harmonic(object): 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): @@ -874,6 +947,14 @@ class Chain(object): #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 @@ -884,14 +965,23 @@ class Mux(object): 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) @@ -946,6 +1036,16 @@ class Mux(object): #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) @@ -959,27 +1059,37 @@ class AD524x(object): 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 @@ -1030,7 +1140,7 @@ class AD524x(object): 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 @@ -1040,7 +1150,7 @@ class AD524x(object): v = truth(value) self._gpo2 = v if immediate or (immediate is None and self.immediate): - self.sendGPO() + self.updateGPO() @property def instruction(self): @@ -1110,6 +1220,15 @@ class AD524x(object): 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 @@ -1565,6 +1684,13 @@ class M25PExx(object): self.programPage(a, data[s:e]) +class NullSPI(SPI): + def write(*args, **kwargs): + pass + + def exchange(*args, **kwargs): + pass + class AtoiSPI0(SPI): @@ -1645,17 +1771,24 @@ class DigiReg(AD524x): 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) @@ -1690,6 +1823,7 @@ class DigiReg(AD524x): > 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))) @@ -1701,6 +1835,41 @@ class DigiReg(AD524x): 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.""" @@ -1721,10 +1890,38 @@ class DAC_atoi(DAC8568): 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 @@ -1765,5 +1962,15 @@ class DAC_atoi(DAC8568): 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