Fix potential threading issues with resid and residfp.

This commit is contained in:
Chris Moeller 2016-05-18 20:27:05 -07:00
parent f3b44f7730
commit 7200229af3
5 changed files with 254 additions and 232 deletions

View file

@ -17,6 +17,8 @@
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// ---------------------------------------------------------------------------
#include <mutex>
#define RESID_ENVELOPE_CC
#include "envelope.h"
@ -156,18 +158,22 @@ unsigned short EnvelopeGenerator::model_dac[2][1 << 8] = {
// ----------------------------------------------------------------------------
// Constructor.
// ----------------------------------------------------------------------------
static std::mutex g_class_init_mutex;
static bool g_class_init = false;
EnvelopeGenerator::EnvelopeGenerator()
{
static bool class_init;
{
std::lock_guard<std::mutex> guard(g_class_init_mutex);
if (!g_class_init) {
// Build DAC lookup tables for 8-bit DACs.
// MOS 6581: 2R/R ~ 2.20, missing termination resistor.
build_dac_table(model_dac[0], 8, 2.20, false);
// MOS 8580: 2R/R ~ 2.00, correct termination.
build_dac_table(model_dac[1], 8, 2.00, true);
if (!class_init) {
// Build DAC lookup tables for 8-bit DACs.
// MOS 6581: 2R/R ~ 2.20, missing termination resistor.
build_dac_table(model_dac[0], 8, 2.20, false);
// MOS 8580: 2R/R ~ 2.00, correct termination.
build_dac_table(model_dac[1], 8, 2.00, true);
class_init = true;
g_class_init = true;
}
}
set_chip_model(MOS6581);

View file

@ -17,6 +17,8 @@
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// ---------------------------------------------------------------------------
#include <mutex>
#define RESID_FILTER_CC
#ifdef _M_ARM
@ -191,231 +193,235 @@ Filter::model_filter_t Filter::model_filter[2];
// ----------------------------------------------------------------------------
// Constructor.
// ----------------------------------------------------------------------------
static std::mutex g_class_init_mutex;
static bool g_class_init = false;
Filter::Filter()
{
static bool class_init;
{
std::lock_guard<std::mutex> guard(g_class_init_mutex);
if (!g_class_init) {
// Temporary table for op-amp transfer function.
int* opamp = new int[1 << 16];
if (!class_init) {
// Temporary table for op-amp transfer function.
int* opamp = new int[1 << 16];
for (int m = 0; m < 2; m++) {
model_filter_init_t& fi = model_filter_init[m];
model_filter_t& mf = model_filter[m];
for (int m = 0; m < 2; m++) {
model_filter_init_t& fi = model_filter_init[m];
model_filter_t& mf = model_filter[m];
// Convert op-amp voltage transfer to 16 bit values.
double vmin = fi.opamp_voltage[0][0];
double opamp_max = fi.opamp_voltage[0][1];
double kVddt = fi.k*(fi.Vdd - fi.Vth);
double vmax = kVddt < opamp_max ? opamp_max : kVddt;
double denorm = vmax - vmin;
double norm = 1.0/denorm;
// Convert op-amp voltage transfer to 16 bit values.
double vmin = fi.opamp_voltage[0][0];
double opamp_max = fi.opamp_voltage[0][1];
double kVddt = fi.k*(fi.Vdd - fi.Vth);
double vmax = kVddt < opamp_max ? opamp_max : kVddt;
double denorm = vmax - vmin;
double norm = 1.0/denorm;
// Scaling and translation constants.
double N16 = norm*((1u << 16) - 1);
double N30 = norm*((1u << 30) - 1);
double N31 = norm*((1u << 31) - 1);
mf.vo_N16 = (int)(N16); // FIXME: Remove?
// Scaling and translation constants.
double N16 = norm*((1u << 16) - 1);
double N30 = norm*((1u << 30) - 1);
double N31 = norm*((1u << 31) - 1);
mf.vo_N16 = (int)(N16); // FIXME: Remove?
// The "zero" output level of the voices.
// The digital range of one voice is 20 bits; create a scaling term
// for multiplication which fits in 11 bits.
double N14 = norm*(1u << 14);
mf.voice_scale_s14 = (int)(N14*fi.voice_voltage_range);
mf.voice_DC = (int)(N16*(fi.voice_DC_voltage - vmin));
// The "zero" output level of the voices.
// The digital range of one voice is 20 bits; create a scaling term
// for multiplication which fits in 11 bits.
double N14 = norm*(1u << 14);
mf.voice_scale_s14 = (int)(N14*fi.voice_voltage_range);
mf.voice_DC = (int)(N16*(fi.voice_DC_voltage - vmin));
// Vdd - Vth, normalized so that translated values can be subtracted:
// k*Vddt - x = (k*Vddt - t) - (x - t)
mf.kVddt = (int)(N16*(kVddt - vmin) + 0.5);
// Vdd - Vth, normalized so that translated values can be subtracted:
// k*Vddt - x = (k*Vddt - t) - (x - t)
mf.kVddt = (int)(N16*(kVddt - vmin) + 0.5);
// Normalized snake current factor, 1 cycle at 1MHz.
// Fit in 5 bits.
mf.n_snake = (int)(denorm*(1 << 13)*(fi.uCox/(2*fi.k)*fi.WL_snake*1.0e-6/fi.C) + 0.5);
// Normalized snake current factor, 1 cycle at 1MHz.
// Fit in 5 bits.
mf.n_snake = (int)(denorm*(1 << 13)*(fi.uCox/(2*fi.k)*fi.WL_snake*1.0e-6/fi.C) + 0.5);
// Create lookup table mapping op-amp voltage across output and input
// to input voltage: vo - vx -> vx
// FIXME: No variable length arrays in ISO C++, hardcoding to max 50
// points.
// double_point scaled_voltage[fi.opamp_voltage_size];
double_point scaled_voltage[50];
// Create lookup table mapping op-amp voltage across output and input
// to input voltage: vo - vx -> vx
// FIXME: No variable length arrays in ISO C++, hardcoding to max 50
// points.
// double_point scaled_voltage[fi.opamp_voltage_size];
double_point scaled_voltage[50];
for (int i = 0; i < fi.opamp_voltage_size; i++) {
// The target output range is 16 bits, in order to fit in an unsigned
// short.
//
// The y axis is temporarily scaled to 31 bits for maximum accuracy in
// the calculated derivative.
//
// Values are normalized using
//
// x_n = m*2^N*(x - xmin)
//
// and are translated back later (for fixed point math) using
//
// m*2^N*x = x_n - m*2^N*xmin
//
scaled_voltage[fi.opamp_voltage_size - 1 - i][0] = int((N16*(fi.opamp_voltage[i][1] - fi.opamp_voltage[i][0]) + (1 << 16))/2 + 0.5);
scaled_voltage[fi.opamp_voltage_size - 1 - i][1] = N31*(fi.opamp_voltage[i][0] - vmin);
}
for (int i = 0; i < fi.opamp_voltage_size; i++) {
// The target output range is 16 bits, in order to fit in an unsigned
// short.
// Clamp x to 16 bits (rounding may cause overflow).
if (scaled_voltage[fi.opamp_voltage_size - 1][0] >= (1 << 16)) {
// The last point is repeated.
scaled_voltage[fi.opamp_voltage_size - 1][0] =
scaled_voltage[fi.opamp_voltage_size - 2][0] = (1 << 16) - 1;
}
interpolate(scaled_voltage, scaled_voltage + fi.opamp_voltage_size - 1,
PointPlotter<int>(opamp), 1.0);
// Store both fn and dfn in the same table.
mf.ak = (int)scaled_voltage[0][0];
mf.bk = (int)scaled_voltage[fi.opamp_voltage_size - 1][0];
int j;
for (j = 0; j < mf.ak; j++) {
opamp[j] = 0;
}
int f = opamp[j] - (opamp[j + 1] - opamp[j]);
for (; j <= mf.bk; j++) {
int fp = f;
f = opamp[j]; // Scaled by m*2^31
// m*2^31*dy/1 = (m*2^31*dy)/(m*2^16*dx) = 2^15*dy/dx
int df = f - fp; // Scaled by 2^15
// High 16 bits (15 bits + sign bit): 2^11*dfn
// Low 16 bits (unsigned): m*2^16*(fn - xmin)
opamp[j] = ((df << (16 + 11 - 15)) & ~0xffff) | (f >> 15);
}
for (; j < (1 << 16); j++) {
opamp[j] = 0;
}
// Create lookup tables for gains / summers.
// 4 bit "resistor" ladders in the bandpass resonance gain and the audio
// output gain necessitate 16 gain tables.
// From die photographs of the bandpass and volume "resistor" ladders
// it follows that gain ~ vol/8 and 1/Q ~ ~res/8 (assuming ideal
// op-amps and ideal "resistors").
for (int n8 = 0; n8 < 16; n8++) {
int n = n8 << 4; // Scaled by 2^7
int x = mf.ak;
for (int vi = 0; vi < (1 << 16); vi++) {
mf.gain[n8][vi] = solve_gain(opamp, n, vi, x, mf);
}
}
// The filter summer operates at n ~ 1, and has 5 fundamentally different
// input configurations (2 - 6 input "resistors").
//
// The y axis is temporarily scaled to 31 bits for maximum accuracy in
// the calculated derivative.
// Note that all "on" transistors are modeled as one. This is not
// entirely accurate, since the input for each transistor is different,
// and transistors are not linear components. However modeling all
// transistors separately would be extremely costly.
int offset = 0;
int size;
for (int k = 0; k < 5; k++) {
int idiv = 2 + k; // 2 - 6 input "resistors".
int n_idiv = idiv << 7; // n*idiv, scaled by 2^7
size = idiv << 16;
int x = mf.ak;
for (int vi = 0; vi < size; vi++) {
mf.summer[offset + vi] =
solve_gain(opamp, n_idiv, vi/idiv, x, mf);
}
offset += size;
}
// The audio mixer operates at n ~ 8/6, and has 8 fundamentally different
// input configurations (0 - 7 input "resistors").
//
// Values are normalized using
//
// x_n = m*2^N*(x - xmin)
//
// and are translated back later (for fixed point math) using
//
// m*2^N*x = x_n - m*2^N*xmin
//
scaled_voltage[fi.opamp_voltage_size - 1 - i][0] = int((N16*(fi.opamp_voltage[i][1] - fi.opamp_voltage[i][0]) + (1 << 16))/2 + 0.5);
scaled_voltage[fi.opamp_voltage_size - 1 - i][1] = N31*(fi.opamp_voltage[i][0] - vmin);
}
// All "on", transistors are modeled as one - see comments above for
// the filter summer.
offset = 0;
size = 1; // Only one lookup element for 0 input "resistors".
for (int l = 0; l < 8; l++) {
int idiv = l; // 0 - 7 input "resistors".
int n_idiv = (idiv << 7)*8/6; // n*idiv, scaled by 2^7
if (idiv == 0) {
// Avoid division by zero; the result will be correct since
// n_idiv = 0.
idiv = 1;
}
int x = mf.ak;
for (int vi = 0; vi < size; vi++) {
mf.mixer[offset + vi] =
solve_gain(opamp, n_idiv, vi/idiv, x, mf);
}
offset += size;
size = (l + 1) << 16;
}
// Clamp x to 16 bits (rounding may cause overflow).
if (scaled_voltage[fi.opamp_voltage_size - 1][0] >= (1 << 16)) {
// The last point is repeated.
scaled_voltage[fi.opamp_voltage_size - 1][0] =
scaled_voltage[fi.opamp_voltage_size - 2][0] = (1 << 16) - 1;
}
// Create lookup table mapping capacitor voltage to op-amp input voltage:
// vc -> vx
for (int m = 0; m < (1 << 16); m++) {
mf.opamp_rev[m] = opamp[m] & 0xffff;
}
interpolate(scaled_voltage, scaled_voltage + fi.opamp_voltage_size - 1,
PointPlotter<int>(opamp), 1.0);
mf.vc_max = (int)(N30*(fi.opamp_voltage[0][1] - fi.opamp_voltage[0][0]));
mf.vc_min = (int)(N30*(fi.opamp_voltage[fi.opamp_voltage_size - 1][1] - fi.opamp_voltage[fi.opamp_voltage_size - 1][0]));
// Store both fn and dfn in the same table.
mf.ak = (int)scaled_voltage[0][0];
mf.bk = (int)scaled_voltage[fi.opamp_voltage_size - 1][0];
int j;
for (j = 0; j < mf.ak; j++) {
opamp[j] = 0;
}
int f = opamp[j] - (opamp[j + 1] - opamp[j]);
for (; j <= mf.bk; j++) {
int fp = f;
f = opamp[j]; // Scaled by m*2^31
// m*2^31*dy/1 = (m*2^31*dy)/(m*2^16*dx) = 2^15*dy/dx
int df = f - fp; // Scaled by 2^15
// High 16 bits (15 bits + sign bit): 2^11*dfn
// Low 16 bits (unsigned): m*2^16*(fn - xmin)
opamp[j] = ((df << (16 + 11 - 15)) & ~0xffff) | (f >> 15);
}
for (; j < (1 << 16); j++) {
opamp[j] = 0;
}
// Create lookup tables for gains / summers.
// 4 bit "resistor" ladders in the bandpass resonance gain and the audio
// output gain necessitate 16 gain tables.
// From die photographs of the bandpass and volume "resistor" ladders
// it follows that gain ~ vol/8 and 1/Q ~ ~res/8 (assuming ideal
// op-amps and ideal "resistors").
for (int n8 = 0; n8 < 16; n8++) {
int n = n8 << 4; // Scaled by 2^7
int x = mf.ak;
for (int vi = 0; vi < (1 << 16); vi++) {
mf.gain[n8][vi] = solve_gain(opamp, n, vi, x, mf);
// DAC table.
int bits = 11;
build_dac_table(mf.f0_dac, bits, fi.dac_2R_div_R, fi.dac_term);
for (int n = 0; n < (1 << bits); n++) {
mf.f0_dac[n] = (unsigned short)(N16*(fi.dac_zero + mf.f0_dac[n]*fi.dac_scale/(1 << bits) - vmin) + 0.5);
}
}
// The filter summer operates at n ~ 1, and has 5 fundamentally different
// input configurations (2 - 6 input "resistors").
//
// Note that all "on" transistors are modeled as one. This is not
// entirely accurate, since the input for each transistor is different,
// and transistors are not linear components. However modeling all
// transistors separately would be extremely costly.
int offset = 0;
int size;
for (int k = 0; k < 5; k++) {
int idiv = 2 + k; // 2 - 6 input "resistors".
int n_idiv = idiv << 7; // n*idiv, scaled by 2^7
size = idiv << 16;
int x = mf.ak;
for (int vi = 0; vi < size; vi++) {
mf.summer[offset + vi] =
solve_gain(opamp, n_idiv, vi/idiv, x, mf);
}
offset += size;
// Free temporary table.
delete[] opamp;
// VCR - 6581 only.
model_filter_init_t& fi = model_filter_init[0];
double N16 = model_filter[0].vo_N16;
double vmin = N16*fi.opamp_voltage[0][0];
double k = fi.k;
double kVddt = N16*(k*(fi.Vdd - fi.Vth));
for (int i = 0; i < (1 << 16); i++) {
// The table index is right-shifted 16 times in order to fit in
// 16 bits; the argument to sqrt is thus multiplied by (1 << 16).
//
// The returned value must be corrected for translation. Vg always
// takes part in a subtraction as follows:
//
// k*Vg - Vx = (k*Vg - t) - (Vx - t)
//
// I.e. k*Vg - t must be returned.
double Vg = kVddt - sqrt((double)i*(1 << 16));
vcr_kVg[i] = (unsigned short)(k*Vg - vmin + 0.5);
}
// The audio mixer operates at n ~ 8/6, and has 8 fundamentally different
// input configurations (0 - 7 input "resistors").
//
// All "on", transistors are modeled as one - see comments above for
// the filter summer.
offset = 0;
size = 1; // Only one lookup element for 0 input "resistors".
for (int l = 0; l < 8; l++) {
int idiv = l; // 0 - 7 input "resistors".
int n_idiv = (idiv << 7)*8/6; // n*idiv, scaled by 2^7
if (idiv == 0) {
// Avoid division by zero; the result will be correct since
// n_idiv = 0.
idiv = 1;
}
int x = mf.ak;
for (int vi = 0; vi < size; vi++) {
mf.mixer[offset + vi] =
solve_gain(opamp, n_idiv, vi/idiv, x, mf);
}
offset += size;
size = (l + 1) << 16;
/*
EKV model:
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))
*/
double kVt = fi.k*fi.Vth;
double Ut = fi.Ut;
double Is = 2*fi.uCox*Ut*Ut/fi.k*fi.WL_vcr;
// Normalized current factor for 1 cycle at 1MHz.
double N15 = N16/2;
double n_Is = N15*1.0e-6/fi.C*Is;
// kVg_Vx = k*Vg - Vx
// I.e. if k != 1.0, Vg must be scaled accordingly.
for (int kVg_Vx = 0; kVg_Vx < (1 << 16); kVg_Vx++) {
double log_term = log1p(exp((kVg_Vx/N16 - kVt)/(2*Ut)));
// Scaled by m*2^15
vcr_n_Ids_term[kVg_Vx] = (unsigned short)(n_Is*log_term*log_term);
}
// Create lookup table mapping capacitor voltage to op-amp input voltage:
// vc -> vx
for (int m = 0; m < (1 << 16); m++) {
mf.opamp_rev[m] = opamp[m] & 0xffff;
}
mf.vc_max = (int)(N30*(fi.opamp_voltage[0][1] - fi.opamp_voltage[0][0]));
mf.vc_min = (int)(N30*(fi.opamp_voltage[fi.opamp_voltage_size - 1][1] - fi.opamp_voltage[fi.opamp_voltage_size - 1][0]));
// DAC table.
int bits = 11;
build_dac_table(mf.f0_dac, bits, fi.dac_2R_div_R, fi.dac_term);
for (int n = 0; n < (1 << bits); n++) {
mf.f0_dac[n] = (unsigned short)(N16*(fi.dac_zero + mf.f0_dac[n]*fi.dac_scale/(1 << bits) - vmin) + 0.5);
}
g_class_init = true;
}
// Free temporary table.
delete[] opamp;
// VCR - 6581 only.
model_filter_init_t& fi = model_filter_init[0];
double N16 = model_filter[0].vo_N16;
double vmin = N16*fi.opamp_voltage[0][0];
double k = fi.k;
double kVddt = N16*(k*(fi.Vdd - fi.Vth));
for (int i = 0; i < (1 << 16); i++) {
// The table index is right-shifted 16 times in order to fit in
// 16 bits; the argument to sqrt is thus multiplied by (1 << 16).
//
// The returned value must be corrected for translation. Vg always
// takes part in a subtraction as follows:
//
// k*Vg - Vx = (k*Vg - t) - (Vx - t)
//
// I.e. k*Vg - t must be returned.
double Vg = kVddt - sqrt((double)i*(1 << 16));
vcr_kVg[i] = (unsigned short)(k*Vg - vmin + 0.5);
}
/*
EKV model:
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))
*/
double kVt = fi.k*fi.Vth;
double Ut = fi.Ut;
double Is = 2*fi.uCox*Ut*Ut/fi.k*fi.WL_vcr;
// Normalized current factor for 1 cycle at 1MHz.
double N15 = N16/2;
double n_Is = N15*1.0e-6/fi.C*Is;
// kVg_Vx = k*Vg - Vx
// I.e. if k != 1.0, Vg must be scaled accordingly.
for (int kVg_Vx = 0; kVg_Vx < (1 << 16); kVg_Vx++) {
double log_term = log1p(exp((kVg_Vx/N16 - kVt)/(2*Ut)));
// Scaled by m*2^15
vcr_n_Ids_term[kVg_Vx] = (unsigned short)(n_Is*log_term*log_term);
}
class_init = true;
}
enable_filter(true);

View file

@ -17,6 +17,8 @@
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// ---------------------------------------------------------------------------
#include <mutex>
#define RESID_WAVE_CC
#include "wave.h"
@ -60,34 +62,38 @@ unsigned short WaveformGenerator::model_dac[2][1 << 12] = {
// ----------------------------------------------------------------------------
// Constructor.
// ----------------------------------------------------------------------------
static std::mutex g_class_init_mutex;
static bool g_class_init = false;
WaveformGenerator::WaveformGenerator()
{
static bool class_init;
{
std::lock_guard<std::mutex> guard(g_class_init_mutex);
if (!g_class_init) {
// Calculate tables for normal waveforms.
accumulator = 0;
for (int i = 0; i < (1 << 12); i++) {
reg24 msb = accumulator & 0x800000;
if (!class_init) {
// Calculate tables for normal waveforms.
accumulator = 0;
for (int i = 0; i < (1 << 12); i++) {
reg24 msb = accumulator & 0x800000;
// Noise mask, triangle, sawtooth, pulse mask.
// The triangle calculation is made branch-free, just for the hell of it.
model_wave[0][0][i] = model_wave[1][0][i] = 0xfff;
model_wave[0][1][i] = model_wave[1][1][i] =
((accumulator ^ -!!msb) >> 11) & 0xffe;
model_wave[0][2][i] = model_wave[1][2][i] = accumulator >> 12;
model_wave[0][4][i] = model_wave[1][4][i] = 0xfff;
// Noise mask, triangle, sawtooth, pulse mask.
// The triangle calculation is made branch-free, just for the hell of it.
model_wave[0][0][i] = model_wave[1][0][i] = 0xfff;
model_wave[0][1][i] = model_wave[1][1][i] =
((accumulator ^ -!!msb) >> 11) & 0xffe;
model_wave[0][2][i] = model_wave[1][2][i] = accumulator >> 12;
model_wave[0][4][i] = model_wave[1][4][i] = 0xfff;
accumulator += 0x1000;
}
accumulator += 0x1000;
// Build DAC lookup tables for 12-bit DACs.
// MOS 6581: 2R/R ~ 2.20, missing termination resistor.
build_dac_table(model_dac[0], 12, 2.20, false);
// MOS 8580: 2R/R ~ 2.00, correct termination.
build_dac_table(model_dac[1], 12, 2.00, true);
class_init = true;
}
// Build DAC lookup tables for 12-bit DACs.
// MOS 6581: 2R/R ~ 2.20, missing termination resistor.
build_dac_table(model_dac[0], 12, 2.20, false);
// MOS 8580: 2R/R ~ 2.00, correct termination.
build_dac_table(model_dac[1], 12, 2.00, true);
class_init = true;
}
sync_source = this;

View file

@ -172,6 +172,8 @@ short calculateCombinedWaveform(CombinedWaveformConfig config, int waveform, int
matrix_t* WaveformCalculator::buildTable(ChipModel model)
{
std::lock_guard<std::mutex> guard(CACHE_LOCK);
const CombinedWaveformConfig* cfgArray = config[model == MOS6581 ? 0 : 1];
cw_cache_t::iterator lb = CACHE.lower_bound(cfgArray);

View file

@ -23,6 +23,7 @@
#define WAVEFORMCALCULATOR_h
#include <map>
#include <mutex>
#include "siddefs-fp.h"
#include "array.h"
@ -95,6 +96,7 @@ private:
typedef std::map<const CombinedWaveformConfig*, matrix_t> cw_cache_t;
private:
std::mutex CACHE_LOCK;
cw_cache_t CACHE;
WaveformCalculator() {}