Cog/Frameworks/libsidplay/sidplay-residfp-code/.svn/pristine/a3/a3cc3cfbb1401e5946c6ba14c75d8c1ecc809c87.svn-base
2014-12-07 22:26:31 -08:00

235 lines
7.4 KiB
Text

/*
* This file is part of libsidplayfp, a SID player engine.
*
* Copyright 2011-2013 Leandro Nini <drfiemost@users.sourceforge.net>
* Copyright 2007-2010 Antti Lankila
* Copyright 2004, 2010 Dag Lem <resid@nimrod.no>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef INTEGRATOR_H
#define INTEGRATOR_H
#include <stdint.h>
#include <cassert>
#include "siddefs-fp.h"
namespace reSIDfp
{
/**
* Find output voltage in inverting integrator SID op-amp circuits, using a
* single fixpoint iteration step.
*
* A circuit diagram of a MOS 6581 integrator is shown below.
*
* ---C---
* | |
* vi -----Rw-------[A>----- vo
* | | vx
* --Rs--
*
* From Kirchoff's current law it follows that
*
* IRw + IRs + ICr = 0
*
* Using the formula for current through a capacitor, i = C*dv/dt, we get
*
* IRw + IRs + C*(vc - vc0)/dt = 0
* dt/C*(IRw + IRs) + vc - vc0 = 0
* vc = vc0 - n*(IRw(vi,vx) + IRs(vi,vx))
*
* which may be rewritten as the following iterative fixpoint function:
*
* vc = vc0 - n*(IRw(vi,g(vc)) + IRs(vi,g(vc)))
*
* To accurately calculate the currents through Rs and Rw, we need to use
* transistor models. Rs has a gate voltage of Vdd = 12V, and can be
* assumed to always be in triode mode. For Rw, the situation is rather
* more complex, as it turns out that this transistor will operate in
* both subthreshold, triode, and saturation modes.
*
* The Shichman-Hodges transistor model routinely used in textbooks may
* be written as follows:
*
* Ids = 0 , Vgst < 0 (subthreshold mode)
* Ids = K/2*W/L*(2*Vgst - Vds)*Vds , Vgst >= 0, Vds < Vgst (triode mode)
* Ids = K/2*W/L*Vgst^2 , Vgst >= 0, Vds >= Vgst (saturation mode)
*
* where
* K = u*Cox (conductance)
* W/L = ratio between substrate width and length
* Vgst = Vg - Vs - Vt (overdrive voltage)
*
* This transistor model is also called the quadratic model.
*
* Note that the equation for the triode mode can be reformulated as
* independent terms depending on Vgs and Vgd, respectively, by the
* following substitution:
*
* Vds = Vgst - (Vgst - Vds) = Vgst - Vgdt
*
* Ids = K*W/L*(2*Vgst - Vds)*Vds
* = K*W/L*(2*Vgst - (Vgst - Vgdt)*(Vgst - Vgdt)
* = K*W/L*(Vgst + Vgdt)*(Vgst - Vgdt)
* = K*W/L*(Vgst^2 - Vgdt^2)
*
* This turns out to be a general equation which covers both the triode
* and saturation modes (where the second term is 0 in saturation mode).
* The equation is also symmetrical, i.e. it can calculate negative
* currents without any change of parameters (since the terms for drain
* and source are identical except for the sign).
*
* FIXME: Subthreshold as function of Vgs, Vgd.
*
* Ids = I0*e^(Vgst/(n*VT)) , Vgst < 0 (subthreshold mode)
*
* The remaining problem with the textbook model is that the transition
* from subthreshold the triode/saturation is not continuous.
*
* Realizing that the subthreshold and triode/saturation modes may both
* be defined by independent (and equal) terms of Vgs and Vds,
* respectively, the corresponding terms can be blended into (equal)
* continuous functions suitable for table lookup.
*
* The EKV model (Enz, Krummenacher and Vittoz) essentially performs this
* blending using an elegant mathematical formulation:
*
* Ids = Is*(if - ir)
* Is = 2*u*Cox*Ut^2/k*W/L
* if = ln^2(1 + e^((k*(Vg - Vt) - Vs)/(2*Ut))
* ir = ln^2(1 + e^((k*(Vg - Vt) - Vd)/(2*Ut))
*
* For our purposes, the EKV model preserves two important properties
* discussed above:
*
* - It consists of two independent terms, which can be represented by
* the same lookup table.
* - It is symmetrical, i.e. it calculates current in both directions,
* facilitating a branch-free implementation.
*
* Rw in the circuit diagram above is a VCR (voltage controlled resistor),
* as shown in the circuit diagram below.
*
* Vw
*
* |
* Vdd |
* |---|
* _|_ |
* -- --| Vg
* | __|__
* | ----- Rw
* | | |
* vi ------------ -------- vo
*
*
* In order to calculalate the current through the VCR, its gate voltage
* must be determined.
*
* Assuming triode mode and applying Kirchoff's current law, we get the
* following equation for Vg:
*
* u*Cox/2*W/L*((Vddt - Vg)^2 - (Vddt - vi)^2 + (Vddt - Vg)^2 - (Vddt - Vw)^2) = 0
* 2*(Vddt - Vg)^2 - (Vddt - vi)^2 - (Vddt - Vw)^2 = 0
* (Vddt - Vg) = sqrt(((Vddt - vi)^2 + (Vddt - Vw)^2)/2)
*
* Vg = Vddt - sqrt(((Vddt - vi)^2 + (Vddt - Vw)^2)/2)
*/
class Integrator
{
private:
const unsigned short* vcr_kVg;
const unsigned short* vcr_n_Ids_term;
const unsigned short* opamp_rev;
unsigned int Vddt_Vw_2;
int vx;
int vc;
const unsigned short kVddt;
const unsigned short n_snake;
public:
Integrator(const unsigned short* vcr_kVg, const unsigned short* vcr_n_Ids_term,
const unsigned short* opamp_rev, unsigned short kVddt, unsigned short n_snake) :
vcr_kVg(vcr_kVg),
vcr_n_Ids_term(vcr_n_Ids_term),
opamp_rev(opamp_rev),
Vddt_Vw_2(0),
vx(0),
vc(0),
kVddt(kVddt),
n_snake(n_snake) {}
void setVw(unsigned short Vw) { Vddt_Vw_2 = (kVddt - Vw) * (kVddt - Vw) >> 1; }
int solve(int vi);
};
} // namespace reSIDfp
#if RESID_INLINING || defined(INTEGRATOR_CPP)
namespace reSIDfp
{
RESID_INLINE
int Integrator::solve(int vi)
{
// "Snake" voltages for triode mode calculation.
const unsigned int Vgst = kVddt - vx;
const unsigned int Vgdt = kVddt - vi;
const unsigned int Vgst_2 = Vgst * Vgst;
const unsigned int Vgdt_2 = Vgdt * Vgdt;
// "Snake" current, scaled by (1/m)*2^13*m*2^16*m*2^16*2^-15 = m*2^30
const int n_I_snake = n_snake * (static_cast<int>(Vgst_2 - Vgdt_2) >> 15);
// VCR gate voltage. // Scaled by m*2^16
// Vg = Vddt - sqrt(((Vddt - Vw)^2 + Vgdt^2)/2)
const int kVg = static_cast<int>(vcr_kVg[(Vddt_Vw_2 + (Vgdt_2 >> 1)) >> 16]);
// VCR voltages for EKV model table lookup.
int Vgs = kVg - vx;
if (Vgs < 0) Vgs = 0;
assert(Vgs < (1 << 16));
int Vgd = kVg - vi;
if (Vgd < 0) Vgd = 0;
assert(Vgd < (1 << 16));
// VCR current, scaled by m*2^15*2^15 = m*2^30
const int n_I_vcr = static_cast<int>(vcr_n_Ids_term[Vgs] - vcr_n_Ids_term[Vgd]) << 15;
// Change in capacitor charge.
vc += n_I_snake + n_I_vcr;
// vx = g(vc)
const int tmp = (vc >> 15) + (1 << 15);
assert(tmp < (1 << 16));
vx = opamp_rev[tmp];
// Return vo.
return vx - (vc >> 14);
}
} // namespace reSIDfp
#endif
#endif