1 /*
2 * This file is part of libsidplayfp, a SID player engine.
3 *
4 * Copyright 2011-2020 Leandro Nini <drfiemost@users.sourceforge.net>
5 * Copyright 2007-2010 Antti Lankila
6 * Copyright 2004, 2010 Dag Lem <resid@nimrod.no>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 */
22
23 #ifndef INTEGRATOR8580_H
24 #define INTEGRATOR8580_H
25
26 #include <stdint.h>
27 #include <cassert>
28
29 #include "siddefs-fp.h"
30
31 namespace reSIDfp
32 {
33
34 /**
35 * 8580 integrator
36 *
37 * +---C---+
38 * | |
39 * vi -----Rfc---o--[A>--o-- vo
40 * vx
41 *
42 * IRfc + ICr = 0
43 * IRfc + C*(vc - vc0)/dt = 0
44 * dt/C*(IRfc) + vc - vc0 = 0
45 * vc = vc0 - n*(IRfc(vi,vx))
46 * vc = vc0 - n*(IRfc(vi,g(vc)))
47 *
48 * IRfc = K*W/L*(Vgst^2 - Vgdt^2) = n*((Vddt - vx)^2 - (Vddt - vi)^2)
49 *
50 * Rfc gate voltage is generated by an OP Amp and depends on chip temperature.
51 */
52 class Integrator8580
53 {
54 private:
55 const unsigned short* opamp_rev;
56
57 mutable int vx;
58 mutable int vc;
59
60 unsigned short nVgt;
61 unsigned short n_dac;
62
63 const double Vth;
64 const double nKp;
65 const double vmin;
66 const double N16;
67
68 public:
Integrator8580(const unsigned short * opamp_rev,double Vth,double nKp,double vmin,double N16)69 Integrator8580(const unsigned short* opamp_rev, double Vth, double nKp, double vmin, double N16) :
70 opamp_rev(opamp_rev),
71 vx(0),
72 vc(0),
73 Vth(Vth),
74 nKp(nKp),
75 vmin(vmin),
76 N16(N16)
77 {
78 setV(1.5);
79 }
80
setFc(double wl)81 void setFc(double wl)
82 {
83 // Normalized current factor, 1 cycle at 1MHz.
84 // Fit in 5 bits.
85 const double tmp = (1 << 13) * nKp * wl;
86 assert(tmp > -0.5 && tmp < 65535.5);
87 n_dac = static_cast<unsigned short>(tmp + 0.5);
88 }
89
90 /**
91 * Set FC gate voltage multiplier.
92 */
setV(double v)93 void setV(double v)
94 {
95 // Gate voltage is controlled by the switched capacitor voltage divider
96 // Ua = Ue * v = 4.76v 1<v<2
97 const double Vg = 4.76 * v;
98 const double Vgt = Vg - Vth;
99
100 // Vg - Vth, normalized so that translated values can be subtracted:
101 // Vgt - x = (Vgt - t) - (x - t)
102 const double tmp = N16 * (Vgt - vmin);
103 assert(tmp > -0.5 && tmp < 65535.5);
104 nVgt = static_cast<unsigned short>(tmp + 0.5);
105 }
106
107 int solve(int vi) const;
108 };
109
110 } // namespace reSIDfp
111
112 #if RESID_INLINING || defined(INTEGRATOR8580_CPP)
113
114 namespace reSIDfp
115 {
116
117 RESID_INLINE
solve(int vi)118 int Integrator8580::solve(int vi) const
119 {
120 // Make sure we're not in subthreshold mode
121 assert(vx < nVgt);
122
123 // DAC voltages
124 const unsigned int Vgst = nVgt - vx;
125 const unsigned int Vgdt = (vi < nVgt) ? nVgt - vi : 0; // triode/saturation mode
126
127 const unsigned int Vgst_2 = Vgst * Vgst;
128 const unsigned int Vgdt_2 = Vgdt * Vgdt;
129
130 // DAC current, scaled by (1/m)*2^13*m*2^16*m*2^16*2^-15 = m*2^30
131 const int n_I_dac = n_dac * (static_cast<int>(Vgst_2 - Vgdt_2) >> 15);
132
133 // Change in capacitor charge.
134 vc += n_I_dac;
135
136 // vx = g(vc)
137 const int tmp = (vc >> 15) + (1 << 15);
138 assert(tmp < (1 << 16));
139 vx = opamp_rev[tmp];
140
141 // Return vo.
142 return vx - (vc >> 14);
143 }
144
145 } // namespace reSIDfp
146
147 #endif
148
149 #endif
150