~arcachofo/simulide/1.1.0

« back to all changes in this revision

Viewing changes to src/gpsim/modules/a2d_v2.cc

  • Committer: arcachofo
  • Date: 2021-01-01 14:23:42 UTC
  • Revision ID: arcachofo@simulide.com-20210101142342-ozfljnll44g5lbl3
Initial Commit 0.5.15-RC3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
   Copyright( C) 2008,2015 Roy R Rankin
 
3
   Copyright( C) 2006 T. Scott Dattalo
 
4
 
 
5
This file is part of the libgpsim library of gpsim
 
6
 
 
7
This library is free software; you can redistribute it and/or
 
8
modify it under the terms of the GNU Lesser General Public
 
9
License as published by the Free Software Foundation; either
 
10
version 2.1 of the License, or( at your option) any later version.
 
11
 
 
12
This library is distributed in the hope that it will be useful,
 
13
but WITHOUT ANY WARRANTY; without even the implied warranty of
 
14
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
15
Lesser General Public License for more details.
 
16
 
 
17
You should have received a copy of the GNU Lesser General Public
 
18
License along with this library; if not, see
 
19
<http://www.gnu.org/licenses/lgpl-2.1.html>.
 
20
*/
 
21
 
 
22
#include "ioports.h"
 
23
#include "pic-processor.h"
 
24
#include "a2d_v2.h"
 
25
 
 
26
//#define p_cpu cpu
 
27
 
 
28
static PinModule InvalidAnInput;
 
29
 
 
30
//#define DEBUG
 
31
#if defined(DEBUG)
 
32
#include "config.h"
 
33
#define Dprintf(arg) {printf("%s:%d ",__FILE__,__LINE__); printf arg; }
 
34
#else
 
35
#define Dprintf(arg) {}
 
36
#endif
 
37
 
 
38
 
 
39
ADCON0_V2::ADCON0_V2(Processor *pCpu, const char *pName)
 
40
    : SfrReg(pCpu, pName),
 
41
      adres(0), adresl(0), adcon1(0), adcon2(0), intcon(0), pir1v2(0),
 
42
      ad_state(AD_IDLE), channel_mask(15),
 
43
      ////ctmu_stim(0),
 
44
      active_stim(-1)
 
45
{
 
46
}
 
47
 
 
48
void ADCON0_V2::setA2DBits( uint nBits ) //  Set resolution of A2D converter
 
49
{
 
50
    m_A2DScale =( 1<<nBits) - 1;
 
51
    m_nBits = nBits;
 
52
}
 
53
 
 
54
void ADCON0_V2::start_conversion(void)
 
55
{
 
56
    uint64_t fc = cpu->currentCycle();
 
57
 
 
58
    Dprintf(("starting A/D conversion\n"));
 
59
 
 
60
    if(!(value.get() & ADON) )
 
61
    {
 
62
        stop_conversion();
 
63
        return;
 
64
    }
 
65
 
 
66
    // Get the A/D Conversion Clock Select bits
 
67
    //
 
68
    // This switch case will get the ADCS bits and set the Tad, or The A/D
 
69
    // converter clock period. Tad is the number of the oscillator periods
 
70
    //  rather instruction cycle periods.
 
71
 
 
72
    Tad = adcon2->get_tad();
 
73
    Tacq = adcon2->get_nacq();
 
74
 
 
75
    if( Tad == 0 ) // RC time source
 
76
    {
 
77
        if( cpu )
 
78
        {
 
79
            Tad =( m_RCtime*cpu->get_frequency());
 
80
            Tad = Tad < 2 ? 2 : Tad;
 
81
        }
 
82
        else Tad = 6;
 
83
    }
 
84
 
 
85
    if( Tacq == 0) fc += 1;    // if Tacq is 0,  go to acqusition on next clock cycle
 
86
 
 
87
    else  fc +=( Tacq * Tad ) / cpu->get_ClockPerInstr();
 
88
 
 
89
    if( ad_state != AD_IDLE )
 
90
    {
 
91
        // The A/D converter is either 'converting' or 'acquiring'
 
92
        // in either case, there is callback break that needs to be moved.
 
93
 
 
94
        stop_conversion();
 
95
        cpu->reassign_break( future_cycle, fc, this );
 
96
    }
 
97
    else cpu->setBreakAbs( fc, this );
 
98
 
 
99
    future_cycle = fc;
 
100
    ad_state = AD_ACQUIRING;
 
101
}
 
102
 
 
103
void ADCON0_V2::stop_conversion(void)
 
104
{
 
105
    ad_state = AD_IDLE;
 
106
}
 
107
 
 
108
void ADCON0_V2::put( uint new_value )
 
109
{
 
110
    uint old_value = value.get();
 
111
    // SET: Reflect it first!
 
112
    value.put(new_value);
 
113
 
 
114
    if( new_value & ADON ) // The A/D converter is being turned on( or maybe left on)
 
115
    {
 
116
        ////
 
117
        /*if( ctmu_stim) // deal with ctmu stimulus for channel change or ON/OFF
 
118
        {
 
119
            if( (old_value ^ new_value) &( ADON|CHS0|CHS1|CHS2|CHS3))
 
120
            {
 
121
                if     ( new_value & ADON)   attach_ctmu_stim();// A/D is on
 
122
                else if(!(new_value & ADON)) detach_ctmu_stim();// A/D is off
 
123
            }
 
124
        }*/
 
125
        if((new_value & ~old_value) & GO) // The 'GO' bit is being turned on, which is request to initiate and A/D conversion
 
126
            start_conversion();
 
127
    }
 
128
    else stop_conversion();
 
129
}
 
130
////
 
131
/*void ADCON0_V2::set_ctmu_stim( stimulus *_ctmu_stim )
 
132
{
 
133
    ctmu_stim = _ctmu_stim;
 
134
    if( value.get() & ADON ) attach_ctmu_stim();
 
135
}*/
 
136
 
 
137
void ADCON0_V2::put_conversion(void)
 
138
{
 
139
    double dRefSep = m_dSampledVrefHi - m_dSampledVrefLo;
 
140
    double dNormalizedVoltage;
 
141
 
 
142
    dNormalizedVoltage =( dRefSep>0.0) ?
 
143
               ( m_dSampledVoltage - m_dSampledVrefLo)/dRefSep : 0.0;
 
144
    dNormalizedVoltage = dNormalizedVoltage>1.0 ? 1.0 : dNormalizedVoltage;
 
145
 
 
146
    uint converted =( uint)(m_A2DScale*dNormalizedVoltage + 0.5);
 
147
 
 
148
    if(adresl)    // non-null for more than 8 bit conversion
 
149
    {
 
150
        if( adcon2->value.get() & ADCON2_V2::ADFM )
 
151
        {
 
152
            adresl->put( converted & 0xff );
 
153
            adres->put( (converted >> 8) & 0x3 );
 
154
        }
 
155
        else
 
156
        {
 
157
            adresl->put( (converted << 6) & 0xc0);
 
158
            adres->put( (converted >> 2) & 0xff);
 
159
        }
 
160
    }
 
161
    else  adres->put((converted ) & 0xff);
 
162
}
 
163
 
 
164
// ADCON0_V2 callback is called when the cycle counter hits the break point that
 
165
// was set in ADCON0_V2::put.
 
166
 
 
167
void ADCON0_V2::callback(void)
 
168
{
 
169
    int channel;
 
170
 
 
171
    switch(ad_state) // The a/d converter is simulated with a state machine.
 
172
    {
 
173
    case AD_IDLE:
 
174
        break;
 
175
 
 
176
    case AD_ACQUIRING:
 
177
        channel =( value.get() >> 2) & channel_mask;
 
178
 
 
179
        m_dSampledVoltage = adcon1->getChannelVoltage( channel );
 
180
        m_dSampledVrefHi  = adcon1->getVrefHi();
 
181
        m_dSampledVrefLo  = adcon1->getVrefLo();
 
182
 
 
183
        future_cycle = cpu->currentCycle() +( (m_nBits+1)*Tad )/cpu->get_ClockPerInstr();
 
184
        cpu->setBreakAbs( future_cycle, this );
 
185
        ad_state = AD_CONVERTING;
 
186
        break;
 
187
 
 
188
    case AD_CONVERTING:
 
189
        put_conversion();
 
190
 
 
191
        value.put(value.get() &( ~GO)); // Clear the GO/!DONE flag.
 
192
        set_interrupt();
 
193
        ad_state = AD_IDLE;
 
194
    }
 
195
}
 
196
 
 
197
//------------------------------------------------------
 
198
//
 
199
void ADCON0_V2::set_interrupt(void)
 
200
{
 
201
    pir1v2->set_adif();
 
202
    intcon->peripheral_interrupt();
 
203
 
 
204
}
 
205
 
 
206
void ADCON0_V2::detach_ctmu_stim()
 
207
{
 
208
    ////
 
209
    /*
 
210
    if( active_stim >=0 && ctmu_stim )
 
211
    {
 
212
        PinModule *pm=adcon1->get_A2Dpin( active_stim );
 
213
        if( pm && pm->getPin().snode && ctmu_stim)
 
214
        {
 
215
            pm->getPin().snode->detach_stimulus(ctmu_stim);
 
216
            pm->getPin().snode->update();
 
217
        }
 
218
    }*/
 
219
    active_stim = -1;
 
220
}
 
221
/* Move ctmu_stim onto currently selected A/D channel input pin.
 
222
   if channel has not changed, just return.
 
223
   Stimulus can only be attached if pin is connected to a node.
 
224
*/
 
225
void ADCON0_V2::attach_ctmu_stim()
 
226
{
 
227
    int channel =( value.get() >> 2) & channel_mask;
 
228
    if     ( active_stim == channel ) return;
 
229
    else if( active_stim >= 0 )       detach_ctmu_stim();
 
230
 
 
231
    PinModule *pm=adcon1->get_A2Dpin(channel);
 
232
 
 
233
    if( pm )
 
234
    {////
 
235
        /*if( !(pm->getPin().snode) )
 
236
        {
 
237
            printf("Warning ADCON0_V2::attach_ctmu_stim %s has no node attached CTMU will not work properly\n", pm->getPin().name_str.c_str());
 
238
        }
 
239
        else if( ctmu_stim )
 
240
        {
 
241
            pm->getPin().snode->attach_stimulus(ctmu_stim);
 
242
            pm->getPin().snode->update();
 
243
            active_stim = channel;
 
244
        }*/
 
245
    }
 
246
}
 
247
 
 
248
 
 
249
//------------------------------------------------------
 
250
// ADCON1
 
251
//
 
252
ADCON1_V2::ADCON1_V2(Processor *pCpu, const char *pName)
 
253
    : SfrReg(pCpu, pName ),
 
254
      m_AnalogPins(0), m_nAnalogChannels(0),
 
255
      mValidCfgBits(0), mCfgBitShift(0), m_vrefHiChan(-1),
 
256
      m_vrefLoChan(-1),  mIoMask(0), m_adcon0(0)
 
257
{
 
258
    for( int i=0; i<(int)cMaxConfigurations; i++)
 
259
        setChannelConfiguration(i, 0);
 
260
}
 
261
ADCON1_V2::~ADCON1_V2()
 
262
{
 
263
    delete [] m_AnalogPins;
 
264
}
 
265
 
 
266
void ADCON1_V2::put( uint new_value )
 
267
{
 
268
    uint new_mask = get_adc_configmask(new_value);
 
269
    uint diff = mIoMask ^ new_mask;
 
270
 
 
271
    Dprintf( ( "ADCON1_V2::put(  %02X ) - new_mask %02X\n", new_value, new_mask ));
 
272
 
 
273
    char newname[20];
 
274
 
 
275
    for(uint i = 0; i < m_nAnalogChannels; i++)
 
276
    {
 
277
        if( (diff &( 1 << i)) && m_AnalogPins[i] != &InvalidAnInput)
 
278
        {
 
279
            if( new_mask &( 1<<i))
 
280
            {
 
281
                snprintf(newname, sizeof(newname), "an%u", i);
 
282
                m_AnalogPins[i]->AnalogReq(this, true, newname);
 
283
            }
 
284
            else
 
285
            {
 
286
                m_AnalogPins[i]->AnalogReq(this, false, "?" /*m_AnalogPins[i]->getPin().name_str.c_str()*/);
 
287
            }
 
288
        }
 
289
    }
 
290
    mIoMask = new_mask;
 
291
    value.put(new_value);
 
292
}
 
293
 
 
294
/*
 
295
 * If A2D uses PCFG, call for each PCFG value( cfg 0 to 15) with
 
296
 * each set bit of bitMask indicating port is an analog port
 
297
 *( either A2D input port or Vref). Processors which use an A2D
 
298
 * method that uses ANSEL register will not call this.
 
299
 *
 
300
 * As an example, for the following Port Configuration Control bit:
 
301
 * PCFG   AN7   AN6   AN5   AN4   AN3   AN2   AN1   AN0
 
302
 * ----   ---- ----- -----  ----- ----- ----- ----- -----
 
303
 * 1100   D    D     D      A     Vref+ Vref- A     A
 
304
 *
 
305
 *  then call setChannelConfiguration with cfg = 12 , bitMask = 0x1f
 
306
 * */
 
307
void ADCON1_V2::setChannelConfiguration( uint cfg, uint bitMask )
 
308
{
 
309
    if( cfg < cMaxConfigurations) m_configuration_bits[cfg] = bitMask;
 
310
}
 
311
 
 
312
/*
 
313
 * Performs same function as setChannelConfiguration, but defines
 
314
 * all entiries in configuration table in one call.
 
315
 */
 
316
 
 
317
void ADCON1_V2::setChanTable(
 
318
        uint m0, uint m1, uint m2, uint m3,
 
319
        uint m4, uint m5, uint m6, uint m7,
 
320
        uint m8, uint m9, uint m10, uint m11,
 
321
        uint m12, uint m13, uint m14, uint m15)
 
322
{
 
323
    m_configuration_bits[0] = m0;
 
324
    m_configuration_bits[1] = m1;
 
325
    m_configuration_bits[2] = m2;
 
326
    m_configuration_bits[3] = m3;
 
327
    m_configuration_bits[4] = m4;
 
328
    m_configuration_bits[5] = m5;
 
329
    m_configuration_bits[6] = m6;
 
330
    m_configuration_bits[7] = m7;
 
331
    m_configuration_bits[8] = m8;
 
332
    m_configuration_bits[9] = m9;
 
333
    m_configuration_bits[10] = m10;
 
334
    m_configuration_bits[11] = m11;
 
335
    m_configuration_bits[12] = m12;
 
336
    m_configuration_bits[13] = m13;
 
337
    m_configuration_bits[14] = m14;
 
338
    m_configuration_bits[15] = m15;
 
339
}
 
340
 
 
341
void ADCON1_V2::setNumberOfChannels( uint nChannels ) // Number of A2D channels processor supports
 
342
{
 
343
    PinModule** save = NULL;
 
344
 
 
345
    if( !nChannels || nChannels <= m_nAnalogChannels) return;
 
346
 
 
347
    if( m_nAnalogChannels && nChannels > m_nAnalogChannels )
 
348
        save = m_AnalogPins;
 
349
 
 
350
    m_AnalogPins = new PinModule *[nChannels];
 
351
 
 
352
    for( uint i=0; i<nChannels; i++ )
 
353
    {
 
354
        if( i < m_nAnalogChannels )
 
355
        {
 
356
            if( save) m_AnalogPins[i] = save[i];
 
357
        }
 
358
        else m_AnalogPins[i] = &InvalidAnInput;
 
359
    }
 
360
    if( save) delete save;
 
361
 
 
362
    m_nAnalogChannels = nChannels;
 
363
}
 
364
 
 
365
/*
 
366
 * Configure use of adcon1 register
 
367
 *      The register is first anded with mask and then shifted
 
368
 *      right shift bits. The result being either PCFG or VCFG
 
369
 *      depending on the type of a2d being used.
 
370
 */
 
371
void ADCON1_V2::setValidCfgBits(uint mask, uint shift)
 
372
{
 
373
    mValidCfgBits = mask;
 
374
    mCfgBitShift = shift;
 
375
}
 
376
 
 
377
/*
 
378
 * get_adc_configmask() is called with the value of the adcon1 register
 
379
 *
 
380
 * if the configuration bit mask is less than 16, the confiiguration bit table
 
381
 * is used to determine if the channel is an analog port.
 
382
 *
 
383
 * Otherwise, each bit in the adcon1 register indicates that the port is
 
384
 * digital(1) or analog(0) aka the 18f1220.
 
385
 *
 
386
 * */
 
387
uint ADCON1_V2::get_adc_configmask(uint reg)
 
388
{
 
389
 
 
390
    if( mValidCfgBits <= 0xf) // use config bit table
 
391
    {
 
392
        return( m_configuration_bits[ (reg>>mCfgBitShift) & mValidCfgBits ] );
 
393
    }
 
394
    else // register directly gives Analog ports( 18f1220)
 
395
    {
 
396
        return( ~(reg >> mCfgBitShift) & mValidCfgBits);
 
397
    }
 
398
}
 
399
 
 
400
/*
 
401
 * Associate a processor I/O pin with an A2D channel
 
402
 */
 
403
void ADCON1_V2::setIOPin(uint channel, PinModule *newPin)
 
404
{
 
405
 
 
406
    if( channel < m_nAnalogChannels &&
 
407
            m_AnalogPins[channel] == &InvalidAnInput && newPin!=0) {
 
408
        m_AnalogPins[channel] = newPin;
 
409
    }
 
410
    else
 
411
    {
 
412
        printf("WARNING %s channel %u, cannot set IOpin\n",__FUNCTION__, channel);
 
413
        if( m_AnalogPins[channel] != &InvalidAnInput)
 
414
            printf("Pin Already assigned\n");
 
415
        else if( channel > m_nAnalogChannels)
 
416
            printf("channel %u >= number of channels %u\n", channel,  m_nAnalogChannels);
 
417
    }
 
418
}
 
419
 
 
420
 
 
421
//------------------------------------------------------
 
422
PinModule *ADCON1_V2::get_A2Dpin(uint channel)
 
423
{
 
424
    if( ( 1<<channel) & get_adc_configmask(value.data) )
 
425
    {
 
426
        PinModule *pm = m_AnalogPins[channel];
 
427
        if( pm != &InvalidAnInput)
 
428
            return pm;
 
429
        cout << "ADCON1_V2::getChannelVoltage channel " << channel <<
 
430
                " not analog\n";
 
431
    }
 
432
    return 0;
 
433
}
 
434
 
 
435
//------------------------------------------------------
 
436
double ADCON1_V2::getChannelVoltage( uint channel )
 
437
{
 
438
    double voltage=0.0;
 
439
 
 
440
    if( channel <= m_nAnalogChannels )
 
441
    {
 
442
        PinModule* pm = get_A2Dpin( channel );
 
443
        if( pm)
 
444
        {
 
445
            ////if( pm->getPin().snode) pm->getPin().snode->update();
 
446
            voltage = pm->getPin().get_nodeVoltage();
 
447
        }
 
448
        else
 
449
        {
 
450
            cout << "ADCON1_V2::getChannelVoltage channel " << channel <<  " not a valid pin\n";
 
451
            voltage = 0.;
 
452
        }
 
453
    }
 
454
    else cout << "ADCON1_V2::getChannelVoltage channel " << channel <<  " > m_nAnalogChannels " << m_nAnalogChannels << "\n";
 
455
 
 
456
    return voltage;
 
457
}
 
458
 
 
459
double ADCON1_V2::getVrefHi()
 
460
{
 
461
    assert(m_vrefHiChan >= 0);    // m_vrefHiChan has not been set
 
462
    if( ( m_adcon0 &&( m_adcon0->value.data & ADCON0_V2::VCFG0)) ||
 
463
        (  !m_adcon0 &&( value.data & VCFG0))) // Use Vref+
 
464
        return(getChannelVoltage(m_vrefHiChan));
 
465
 
 
466
    return cpu->get_Vdd();
 
467
}
 
468
 
 
469
double ADCON1_V2::getVrefLo()
 
470
{
 
471
    assert(m_vrefLoChan >= 0);    // m_vrefLoChan has not been set
 
472
    if( ( m_adcon0 &&( m_adcon0->value.data & ADCON0_V2::VCFG1)) ||
 
473
        (  !m_adcon0 &&( value.data & VCFG1))) // Use Vref-
 
474
        return getChannelVoltage(m_vrefLoChan);
 
475
 
 
476
    return 0.0;
 
477
}
 
478
 
 
479
//------------------------------------------------------
 
480
// ADCON2_V2
 
481
//
 
482
ADCON2_V2::ADCON2_V2(Processor *pCpu, const char *pName )
 
483
    : SfrReg(pCpu, pName )
 
484
{
 
485
}
 
486
 
 
487
char ADCON2_V2::get_nacq()
 
488
{
 
489
    static char acq_tab[8] = { 0, 2, 4, 6, 8, 12, 16, 20};
 
490
    return(acq_tab[((value.get() &( ACQT2 | ACQT1 | ACQT0)) >> 3) ]);
 
491
}
 
492
 
 
493
char ADCON2_V2::get_tad()
 
494
{
 
495
    static char adcs_tab[8] = { 2, 8, 32, 0, 4, 16, 64, 0};
 
496
    return(adcs_tab[(value.get() &( ADCS2 | ADCS1 | ADCS0)) ]);
 
497
}
 
498
 
 
499
bool ADCON2_V2::get_adfm()
 
500
{
 
501
    return((value.get() & ADFM) == ADFM);
 
502
}
 
503
 
 
504
ADCON1_2B::ADCON1_2B(Processor *pCpu, const char *pName ) :
 
505
    ADCON1_V2(pCpu, pName ), Vctmu(0.0), Vdac(0.0), Vfvr_buf2(0)
 
506
{
 
507
}
 
508
 
 
509
//------------------------------------------------------
 
510
PinModule *ADCON1_2B::get_A2Dpin(uint channel)
 
511
{
 
512
    if(channel <= m_nAnalogChannels)
 
513
    {
 
514
        PinModule *pm = m_AnalogPins[channel];
 
515
        if( pm != &InvalidAnInput)
 
516
            return pm;
 
517
        cout << "ADCON1_V2::getChannelVoltage channel " << channel << " not analog\n";
 
518
    }
 
519
    return 0;
 
520
}
 
521
double ADCON1_2B::getChannelVoltage(uint channel)
 
522
{
 
523
    double voltage=0.0;
 
524
 
 
525
    if( channel <= m_nAnalogChannels )
 
526
    {
 
527
        PinModule *pm = get_A2Dpin(channel);
 
528
        if( pm ) voltage = pm->getPin().get_nodeVoltage();
 
529
        else
 
530
        {
 
531
            cout << "ADCON1_2B::getChannelVoltage channel " << channel << " not valid for A2D\n";
 
532
        }
 
533
    }
 
534
    else if( channel == CTMU) voltage = Vctmu;
 
535
    else if( channel == DAC)  voltage = Vdac;
 
536
    else if( channel == FVR_BUF2) voltage = Vfvr_buf2;
 
537
    else cout << "ADCON1_2B::getChannelVoltage channel " << channel << " not valid for A2D\n";
 
538
 
 
539
    return voltage;
 
540
}
 
541
double ADCON1_2B::getVrefHi()
 
542
{
 
543
    assert(m_vrefHiChan >= 0);    // m_vrefHiChan has not been set
 
544
 
 
545
    switch( value.data &( PVCFG1 | PVCFG0))
 
546
    {
 
547
    case 0:                     // use Vdd
 
548
    case( PVCFG1 | PVCFG0 ):    // reserved use Vdd
 
549
        return cpu->get_Vdd();
 
550
        break;
 
551
 
 
552
    case PVCFG0:                // use external pin Vref+
 
553
        return(getChannelVoltage(m_vrefHiChan));
 
554
        break;
 
555
 
 
556
    case PVCFG1:                // use FVR buf2
 
557
        return Vfvr_buf2;
 
558
        break;
 
559
    }
 
560
    return 0.0;
 
561
}
 
562
double ADCON1_2B::getVrefLo()
 
563
{
 
564
    assert(m_vrefLoChan >= 0);    // m_vrefLoChan has not been set
 
565
 
 
566
    // external pin Vref- ?
 
567
    if( (value.data &( NVCFG1 | NVCFG0)) == NVCFG1)
 
568
        return getChannelVoltage(m_vrefLoChan);
 
569
 
 
570
    return 0.0;
 
571
}
 
572
 
 
573
void ADCON1_2B::put(uint new_value)
 
574
{
 
575
    value.put(new_value);
 
576
}
 
577
 
 
578
// Special trigger from ctmu module
 
579
void ADCON1_2B::ctmu_trigger()
 
580
{
 
581
    if( value.data & TRIGSEL)   // special trigger from CTMU active?
 
582
    {
 
583
        assert(m_adcon0);
 
584
        uint value = m_adcon0->value.data;
 
585
        if( (value & ADCON0_V2::ADON))
 
586
        {
 
587
            value |= ADCON0_V2::GO;
 
588
            m_adcon0->put(value);
 
589
        }
 
590
    }
 
591
}
 
592
// Special trigger from cpp module
 
593
void ADCON1_2B::ccp_trigger()
 
594
{
 
595
    if( !(value.data & TRIGSEL))        // special trigger from ccp active?
 
596
    {
 
597
        uint value = m_adcon0->value.data;
 
598
        if( (value & ADCON0_V2::ADON))
 
599
        {
 
600
            value |= ADCON0_V2::GO;
 
601
            m_adcon0->put(value);
 
602
        }
 
603
    }
 
604
}
 
605
 
 
606
ANSEL_2B::ANSEL_2B(Processor *pCpu, const char *pName )
 
607
    : SfrReg(pCpu, pName ), mask(0)
 
608
{
 
609
    for(int i=0; i<8; i++)
 
610
    {
 
611
        analog_channel[i] = -1;
 
612
        m_AnalogPins[i] = &InvalidAnInput;
 
613
    }
 
614
}
 
615
 
 
616
void ANSEL_2B::put(uint new_value)
 
617
{
 
618
    put_value(new_value);
 
619
}
 
620
 
 
621
void ANSEL_2B::put_value( uint new_value )
 
622
{
 
623
    char newname[20];
 
624
    new_value &= mask;
 
625
    uint diff = value.get() ^ new_value;
 
626
 
 
627
    value.put(new_value);
 
628
 
 
629
    for(int i = 0; i < 8; i++)
 
630
    {
 
631
        if( ((1<<i) & diff) &&( m_AnalogPins[i] != &InvalidAnInput))
 
632
        {
 
633
            if( new_value &( 1<<i))
 
634
            {
 
635
                snprintf( newname, sizeof(newname), "an%d", analog_channel[i] );
 
636
                m_AnalogPins[i]->AnalogReq(this, true, newname);
 
637
            }
 
638
            else
 
639
            {
 
640
                m_AnalogPins[i]->AnalogReq( this, false, "?" /*m_AnalogPins[i]->getPin().name_str.c_str()*/);
 
641
            }
 
642
        }
 
643
    }
 
644
}
 
645
void ANSEL_2B::setIOPin( uint channel, PinModule* port, ADCON1_2B* adcon1 )
 
646
{
 
647
    char newname[20];
 
648
    uint pin = port->getPinNumber();
 
649
    m_AnalogPins[pin] = port;
 
650
    analog_channel[pin] = channel;
 
651
    adcon1->setIOPin(channel, port);
 
652
    mask |= 1<<pin;
 
653
 
 
654
    if( (1<<pin) & value.get() )
 
655
    {
 
656
        snprintf(newname, sizeof(newname), "an%u", channel);
 
657
        m_AnalogPins[pin]->AnalogReq(this, true, newname);
 
658
    }
 
659
}
 
660
ANSEL_2A::ANSEL_2A(Processor *pCpu, const char *pName )
 
661
    : ANSEL_2B(pCpu, pName )
 
662
{
 
663
}
 
664
void ANSEL_2A::setIOPin(uint channel, PinModule *port, ADCON1_2B *adcon1)
 
665
{
 
666
    char newname[20];
 
667
    uint bit = channel & 7;
 
668
    m_AnalogPins[bit] = port;
 
669
    analog_channel[bit] = channel;
 
670
    adcon1->setIOPin(channel, port);
 
671
    mask |= 1<<bit;
 
672
 
 
673
    if( (1<<bit) & value.get())
 
674
    {
 
675
        snprintf(newname, sizeof(newname), "an%u", channel);
 
676
        m_AnalogPins[bit]->AnalogReq(this, true, newname);
 
677
    }
 
678
}
 
679
//
 
680
//--------------------------------------------------
 
681
// member functions for the FVRCON_V2 class
 
682
// with one set of gains and FVRST set after delay
 
683
//--------------------------------------------------
 
684
//
 
685
FVRCON_V2::FVRCON_V2(Processor *pCpu, const char *pName, uint bitMask)
 
686
    : SfrReg(pCpu, pName ), future_cycle(0),
 
687
      adcon1(0), daccon0(0), cmModule(0), cpscon0(0)
 
688
{
 
689
    mask_writable = bitMask;
 
690
}
 
691
 
 
692
void  FVRCON_V2::put(uint new_value)
 
693
{
 
694
    uint masked_value =( new_value & mask_writable);
 
695
    put_value(masked_value);
 
696
}
 
697
 
 
698
void  FVRCON_V2::put_value(uint new_value)
 
699
{
 
700
    uint diff = value.get() ^ new_value;
 
701
 
 
702
    if( diff )
 
703
    {
 
704
        if( diff &  FVREN ) new_value &= ~FVRRDY;       // Clear FVRRDY regardless of ON or OFF
 
705
        if( new_value & FVREN ) // Enable ON?
 
706
        {
 
707
            future_cycle = cpu->currentCycle() + 25e-6 /cpu->seconds_per_cycle();
 
708
            cpu->setBreakAbs( future_cycle, this );
 
709
        }
 
710
        else if( future_cycle )
 
711
        {
 
712
            cpu->clear_break(this);
 
713
            future_cycle = 0;
 
714
        }
 
715
    }
 
716
    value.put( new_value );
 
717
    compute_FVR( new_value );
 
718
 
 
719
    update();
 
720
}
 
721
 
 
722
// Set FVRRDY bit after timeout
 
723
void FVRCON_V2::callback()
 
724
{
 
725
    future_cycle = 0;
 
726
    put_value(value.get() | FVRRDY);
 
727
}
 
728
 
 
729
 
 
730
 
 
731
double FVRCON_V2::compute_FVR(uint fvrcon)
 
732
{
 
733
    double ret = -1.;
 
734
 
 
735
    if( fvrcon & FVRRDY )
 
736
    {
 
737
        switch(fvrcon &( FVRS0 | FVRS1))
 
738
        {
 
739
        case 0:         // output is off
 
740
            ret = 0.0;
 
741
            break;
 
742
 
 
743
        case FVRS0:     // Gain = 1
 
744
            ret = 1.024;
 
745
            break;
 
746
 
 
747
        case FVRS1:     // Gain = 2
 
748
            ret = 2.048;
 
749
            break;
 
750
 
 
751
        case( FVRS0|FVRS1): // Gain = 4
 
752
            ret = 4.096;
 
753
            break;
 
754
        }
 
755
    }
 
756
    if( ret > cpu->get_Vdd() )
 
757
    {
 
758
        cerr << "warning FVRCON FVRAD("<< ret <<") > Vdd(" <<cpu->get_Vdd() << ")\n";
 
759
        ret = -1.;
 
760
    }
 
761
    for( uint i=0; i<daccon0_list.size(); i++)
 
762
    {
 
763
        daccon0_list[i]->set_FVR_CDA_volt(ret);
 
764
    }
 
765
    if( adcon1 ) adcon1->setVoltRef( ret );
 
766
    if( cmModule ) cmModule->set_FVR_volt( ret );
 
767
    if( cpscon0 ) cpscon0->set_FVR_volt( ret );
 
768
    return ret;
 
769
}
 
770
 
 
771
void  DACCON0_V2::compute_dac(uint value)
 
772
{
 
773
    double Vhigh = get_Vhigh(value);
 
774
    double Vlow = 0.;
 
775
    double Vout;
 
776
 
 
777
    if( value & DACEN ) // DAC is enabled
 
778
    {
 
779
        Dprintf(("DACCON0_V2::compute_dac Vhigh %.2f daccon1_reg %x\n", Vhigh, daccon1_reg));
 
780
        Vout =(Vhigh-Vlow)*daccon1_reg/bit_resolution - Vlow;
 
781
    }
 
782
    else if( value & DACLPS) Vout = Vhigh;
 
783
    else                     Vout = Vlow;
 
784
 
 
785
    set_dacoutpin(value & DACOE, 0, Vout);
 
786
 
 
787
    if( adcon1 ) adcon1->update_dac( Vout );
 
788
    if( cmModule ) cmModule->set_DAC_volt( Vout );
 
789
    if( cpscon0 ) cpscon0->set_DAC_volt( Vout );
 
790
}
 
791
 
 
792
double DACCON0_V2::get_Vhigh(uint value)
 
793
{
 
794
    uint mode =( value &( DACPSS0|DACPSS1)) >> 2;
 
795
    switch(mode)
 
796
    {
 
797
    case 0:     // Vdd
 
798
        return cpu->get_Vdd();
 
799
 
 
800
    case 1:     // Vref+ pin, get is from A2D setup
 
801
        if(adcon1)
 
802
            return(adcon1->getChannelVoltage(adcon1->getVrefHiChan()));
 
803
        cerr << "ERROR DACCON0 DACPSS=01b adcon1 not set\n";
 
804
        return 0.;
 
805
 
 
806
    case 2:     // Fixed Voltage Reference
 
807
        return FVR_CDA_volt;
 
808
 
 
809
    case 3:     // Reserved value
 
810
        cerr << "ERROR DACCON0 DACPSS=11b is reserved value\n";
 
811
        return 0.;
 
812
    }
 
813
    return 0.;  // cant get here
 
814