~arcachofo/simulide/1.1.0

« back to all changes in this revision

Viewing changes to src/gpsim/modules/tmr0.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) 1998 Scott Dattalo
 
3
 
 
4
This file is part of the libgpsim library of gpsim
 
5
 
 
6
This library is free software; you can redistribute it and/or
 
7
modify it under the terms of the GNU Lesser General Public
 
8
License as published by the Free Software Foundation; either
 
9
version 2.1 of the License, or (at your option) any later version.
 
10
 
 
11
This library is distributed in the hope that it will be useful,
 
12
but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
14
Lesser General Public License for more details.
 
15
 
 
16
You should have received a copy of the GNU Lesser General Public
 
17
License along with this library; if not, see
 
18
<http://www.gnu.org/licenses/lgpl-2.1.html>.
 
19
*/
 
20
 
 
21
 
 
22
#include <stdio.h>
 
23
#include <iostream>
 
24
#include <iomanip>
 
25
#include <QDebug>
 
26
#include <string>
 
27
 
 
28
#include "14bit-processors.h"
 
29
#include "14bit-tmrs.h"
 
30
#include "a2dconverter.h"
 
31
#include "clc.h"
 
32
 
 
33
 
 
34
//#define DEBUG
 
35
#if defined(DEBUG)
 
36
#define Dprintf(arg) {printf("%s:%d-%s() ",__FILE__,__LINE__,__FUNCTION__); printf arg; }
 
37
#else
 
38
#define Dprintf(arg) {}
 
39
#endif
 
40
 
 
41
//--------------------------------------------------
 
42
// member functions for the TMR0 base class
 
43
//--------------------------------------------------
 
44
TMR0::TMR0(Processor *pCpu, const char *pName )
 
45
  : SfrReg(pCpu,pName),
 
46
    prescale(1), prescale_counter(0), old_option(0),
 
47
    state(STOPPED), synchronized_cycle(0),
 
48
    future_cycle(0), last_cycle(0),
 
49
    m_pOptionReg(0), m_t1gcon(0), m_adcon2(0),
 
50
    m_bLastClockedState(false), t0xcs(false)
 
51
{
 
52
  value.put(0);
 
53
 
 
54
  new_name("tmr0");
 
55
  for (int i = 0; i < 4; i++)
 
56
    m_clc[i] = 0;
 
57
}
 
58
 
 
59
//------------------------------------------------------------------------
 
60
void TMR0::release()
 
61
{
 
62
}
 
63
//------------------------------------------------------------------------
 
64
// setSinkState
 
65
//
 
66
// Called when the I/O pin driving TMR0 changes states.
 
67
 
 
68
void TMR0::setSinkState(char new3State)
 
69
{
 
70
  bool bNewState = new3State == '1';
 
71
 
 
72
  if (m_bLastClockedState != bNewState) {
 
73
    m_bLastClockedState = bNewState;
 
74
 
 
75
    if (get_t0cs() && !get_t0xcs() && bNewState != get_t0se())
 
76
      increment();
 
77
  }
 
78
}
 
79
 
 
80
void TMR0::set_cpu(Processor *new_cpu, PortRegister *reg, uint pin, OPTION_REG *pOption)
 
81
{
 
82
  cpu = new_cpu;
 
83
  m_pOptionReg = pOption;
 
84
  reg->addSink(this,pin);
 
85
}
 
86
 
 
87
// RCP - add an alternate way to connect to a CPU
 
88
void TMR0::set_cpu(Processor *new_cpu, PinModule *pin,OPTION_REG *pOption)
 
89
{
 
90
  cpu = new_cpu;
 
91
  m_pOptionReg = pOption;
 
92
  pin->addSink(this);
 
93
}
 
94
 
 
95
//------------------------------------------------------------
 
96
// Stop the tmr.
 
97
//
 
98
void TMR0::stop()
 
99
{
 
100
 
 
101
  Dprintf(("\n"));
 
102
 
 
103
  // If tmr0 is running, then stop it:
 
104
  if (state & RUNNING) {
 
105
 
 
106
    // refresh the current value.
 
107
    get_value();
 
108
 
 
109
    state &= (~RUNNING);      // the timer is disabled.
 
110
    clear_trigger();
 
111
 
 
112
  }
 
113
}
 
114
 
 
115
void TMR0::start(int restart_value, int sync)
 
116
{
 
117
 
 
118
  Dprintf(("restart_value=%d(0x%x) sync=%d\n",restart_value, restart_value, sync));
 
119
 
 
120
  state |= RUNNING;          // the timer is on
 
121
 
 
122
  value.put(restart_value&0xff);
 
123
 
 
124
  //old_option = cpu_pic->option_reg.value.get();
 
125
  old_option = m_pOptionReg->get_value();
 
126
 
 
127
  prescale = 1 << get_prescale();
 
128
  prescale_counter = prescale;
 
129
 
 
130
  if(get_t0cs()) {
 
131
    Dprintf(("External clock\n"));
 
132
 
 
133
  } 
 
134
  else 
 
135
  {
 
136
    synchronized_cycle = cpu->currentCycle() + sync;
 
137
 
 
138
    last_cycle = (restart_value % max_counts()) * prescale;
 
139
    last_cycle = synchronized_cycle - last_cycle;
 
140
 
 
141
    uint64_t fc = last_cycle + max_counts() * prescale;
 
142
 
 
143
    if(future_cycle) cpu->reassign_break( future_cycle, fc, this );
 
144
    else             cpu->setBreakAbs( fc, this );
 
145
 
 
146
    future_cycle = fc;
 
147
 
 
148
    Dprintf(("last_cycle:0x%" PRINTF_GINT64_MODIFIER "x future_cycle:0x%" PRINTF_GINT64_MODIFIER "x\n",last_cycle,future_cycle));
 
149
  }
 
150
}
 
151
 
 
152
void TMR0::clear_trigger()
 
153
{
 
154
  Dprintf(("\n"));
 
155
 
 
156
  if (future_cycle) {
 
157
    future_cycle = 0;
 
158
    cpu->clear_break( this );
 
159
  }
 
160
  last_cycle = 0;
 
161
}
 
162
 
 
163
uint TMR0::get_prescale()
 
164
{
 
165
  Dprintf(("OPTION::PSA=%u\n", m_pOptionReg->get_psa()));
 
166
 
 
167
  //return (cpu_pic->option_reg.get_psa() ? 0 : (1+cpu_pic->option_reg.get_prescale()));
 
168
  return (m_pOptionReg->get_psa()  ? 0 : (1+m_pOptionReg->get_prescale()));
 
169
}
 
170
 
 
171
// This is used to drive timer ias counter of IO port if T0CS is true
 
172
//
 
173
void TMR0::increment()
 
174
{
 
175
  Dprintf(("\n"));
 
176
 
 
177
  if((state & RUNNING) == 0)
 
178
    return;
 
179
 
 
180
  if(--prescale_counter == 0)
 
181
    {
 
182
      prescale_counter = prescale;
 
183
      if(value.get() >= (max_counts()-1))
 
184
        {
 
185
          //cout << "TMR0 rollover because of external clock ";
 
186
          value.put(0);
 
187
          set_t0if();
 
188
 
 
189
        }
 
190
      else
 
191
        value.put(value.get() + 1);
 
192
    }
 
193
  //  cout << "TMR0 value ="<<value.get() << '\n';
 
194
}
 
195
 
 
196
 
 
197
void TMR0::put_value(uint new_value)
 
198
{
 
199
  Dprintf(("\n"));
 
200
 
 
201
  value.put(new_value & 0xff);
 
202
 
 
203
  // If tmr0 is enabled, then start it up.
 
204
  if(state & RUNNING)
 
205
    start(new_value,2);
 
206
}
 
207
 
 
208
void TMR0::put(uint new_value)
 
209
{
 
210
  Dprintf(("\n"));
 
211
 
 
212
  put_value(new_value);
 
213
}
 
214
 
 
215
uint TMR0::get_value()
 
216
{
 
217
  // If the TMR0 is being read immediately after being written, then
 
218
  // it hasn't had enough time to synchronize with the PIC's clock.
 
219
  if(cpu->currentCycle() <= synchronized_cycle)
 
220
    return value.get();
 
221
 
 
222
  // If we're running off the external clock or the tmr is disabled
 
223
  // then just return the register value.
 
224
  if(get_t0cs() || ((state & RUNNING)==0))
 
225
    return(value.get());
 
226
 
 
227
  int new_value = (int )((cpu->currentCycle() - last_cycle)/ prescale);
 
228
 
 
229
  if (new_value == (int)max_counts())
 
230
  {
 
231
    // tmr0 is about to roll over. However, the user code
 
232
    // has requested the current value before the callback function
 
233
    // has been invoked. So do callback and return 0.
 
234
     if (future_cycle)
 
235
     {
 
236
        future_cycle = 0;
 
237
        cpu->clear_break( this );
 
238
        callback();
 
239
     }
 
240
     new_value = 0;
 
241
  }
 
242
 
 
243
  if (new_value >= (int)max_counts())
 
244
    {
 
245
      cout << "TMR0: bug TMR0 is larger than " <<  max_counts() - 1  << "...\n";
 
246
      cout << "cycles.value = " << cpu->currentCycle() <<
 
247
        "  last_cycle = " << last_cycle <<
 
248
        "  prescale = "  << prescale <<
 
249
        "  calculated value = " << new_value << '\n';
 
250
 
 
251
      // cop out. tmr0 has a bug. So rather than annoy
 
252
      // the user with an infinite number of messages,
 
253
      // let's just go ahead and reset the logic.
 
254
      new_value &= 0xff;
 
255
      last_cycle = new_value*prescale;
 
256
      last_cycle = cpu->currentCycle() - last_cycle;
 
257
      synchronized_cycle = last_cycle;
 
258
    }
 
259
 
 
260
  value.put(new_value);
 
261
  return(value.get());
 
262
 
 
263
}
 
264
 
 
265
uint TMR0::get()
 
266
{
 
267
  value.put(get_value());
 
268
  return value.get();
 
269
}
 
270
void TMR0::new_prescale()
 
271
{
 
272
  Dprintf(("\n"));
 
273
 
 
274
  uint new_value;
 
275
 
 
276
  int option_diff = old_option ^ m_pOptionReg->get_value();
 
277
 
 
278
  old_option ^= option_diff;   // save old option value. ( (a^b) ^b = a)
 
279
 
 
280
  if(option_diff & OPTION_REG::T0CS) 
 
281
  {
 
282
    // TMR0's clock source has changed.
 
283
 
 
284
    if(m_pOptionReg->get_t0cs()) 
 
285
    {
 
286
      // External clock
 
287
      if (future_cycle)
 
288
      {
 
289
        future_cycle = 0;
 
290
        cpu->clear_break(this);
 
291
      }
 
292
    }
 
293
    start(value.get());
 
294
 
 
295
  } 
 
296
  else 
 
297
  {
 
298
    // Refresh the current tmr0 value. The current tmr0 value is used
 
299
    // below to recompute the value for 'last_cycle'
 
300
    get_value();
 
301
 
 
302
    if(get_t0cs() || ((state & RUNNING)==0)) 
 
303
    {
 
304
      prescale = 1 << get_prescale();
 
305
      prescale_counter = prescale;
 
306
    } 
 
307
    else 
 
308
    {
 
309
      if(last_cycle < (int64_t)cpu->currentCycle())
 
310
        new_value = (uint)((cpu->currentCycle() - last_cycle)/prescale);
 
311
      else
 
312
        new_value = 0;
 
313
 
 
314
      if(new_value>=max_counts()) 
 
315
      {
 
316
        cout << "TMR0 bug (new_prescale): exceeded max count"<< max_counts() <<'\n';
 
317
        cout << "   last_cycle = 0x" << hex << last_cycle << endl;
 
318
        cout << "   cpu cycle = 0x" << hex << (cpu->currentCycle()) << endl;
 
319
 
 
320
        cout << "   prescale = 0x" << hex << prescale << endl;
 
321
      }
 
322
      // Get the current value of TMR0
 
323
      // cout << "cycles " << cycles.value  << " old prescale " << prescale;
 
324
 
 
325
      prescale = 1 << get_prescale();
 
326
      prescale_counter = prescale;
 
327
 
 
328
      last_cycle = value.get() * prescale;
 
329
      last_cycle = cpu->currentCycle() - last_cycle;
 
330
      synchronized_cycle = last_cycle;
 
331
 
 
332
      uint64_t fc = last_cycle + max_counts() * prescale;
 
333
 
 
334
      cpu->reassign_break( future_cycle, fc, this );
 
335
 
 
336
      future_cycle = fc;
 
337
    }
 
338
  }
 
339
}
 
340
 
 
341
bool TMR0::get_t0cs()
 
342
{
 
343
 
 
344
  //return cpu_pic->option_reg.get_t0cs() != 0;
 
345
  return m_pOptionReg->get_t0cs() != 0;
 
346
}
 
347
bool TMR0::get_t0se()
 
348
{
 
349
  //return cpu_pic->option_reg.get_t0se() != 0;
 
350
  return m_pOptionReg->get_t0se() != 0;
 
351
}
 
352
 
 
353
void TMR0::set_t0if()
 
354
{
 
355
  if(cpu_pic->base_isa() == _14BIT_PROCESSOR_ ||
 
356
        cpu_pic->base_isa() == _14BIT_E_PROCESSOR_)
 
357
    {
 
358
      cpu14->intcon->set_t0if();
 
359
    }
 
360
  if (m_t1gcon)
 
361
  {
 
362
        m_t1gcon->T0_gate(true);
 
363
        // Spec sheet does not indicate when the overflow signal
 
364
        // is cleared, so I am assuming it is just a pulse. RRR
 
365
        m_t1gcon->T0_gate(false);
 
366
  }
 
367
  if (m_adcon2) m_adcon2->t0_overflow();
 
368
 
 
369
  for(int i =0; i < 4; i++)
 
370
        if (m_clc[i]) m_clc[i]->t0_overflow();
 
371
}
 
372
 
 
373
void TMR0::set_clc( CLC *_clc, int index ) 
 
374
 
375
    qDebug()<<"TMR0::set_clc"<<index;
 
376
    m_clc[index] = _clc; 
 
377
}
 
378
 
 
379
// TMR0 callback is called when the cycle counter hits the break point that
 
380
// was set in TMR0::put. The cycle counter will clear the break point, so
 
381
// we don't need to worry about it. At this point, TMR0 is rolling over.
 
382
 
 
383
void TMR0::callback()
 
384
{
 
385
 
 
386
  Dprintf(("now=0x%" PRINTF_GINT64_MODIFIER "x\n",cpu->get_cycles()->get()));
 
387
 
 
388
  if((state & RUNNING) == 0) {
 
389
 
 
390
    cout << "TMR0 callback ignored because timer is disabled\n";
 
391
  }
 
392
 
 
393
  // If tmr0 is being clocked by the external clock, then at some point
 
394
  // the simulate code must have switched from the internal clock to
 
395
  // external clock. The cycle break point was still set, so just ignore it.
 
396
  if(get_t0cs())
 
397
    {
 
398
      future_cycle = 0;  // indicates that tmr0 no longer has a break point
 
399
      return;
 
400
    }
 
401
 
 
402
  value.put(0);
 
403
  synchronized_cycle = cpu->currentCycle();
 
404
  last_cycle = synchronized_cycle;
 
405
  future_cycle = last_cycle + max_counts()*prescale;
 
406
  cpu->setBreakAbs( future_cycle, this );
 
407
  set_t0if();
 
408
}
 
409
 
 
410
void  TMR0::reset(RESET_TYPE r)
 
411
{
 
412
  switch(r) {
 
413
  case POR_RESET:
 
414
    value = por_value;
 
415
    break;
 
416
  default:
 
417
    break;
 
418
  }
 
419
}
 
420
 
 
421
void TMR0::callback_print()
 
422
{
 
423
  cout << "TMR0\n";
 
424
}
 
425
 
 
426
// Suspend TMR0 for sleep
 
427
void TMR0::sleep()
 
428
{
 
429
    if((state & RUNNING))
 
430
    {
 
431
        stop();
 
432
        state = SLEEPING;
 
433
    }
 
434
}
 
435
// wake up TMR0 when sleep command terminates
 
436
void TMR0::wake()
 
437
{
 
438
    if ((state & SLEEPING))
 
439
    {
 
440
        if (! (state & RUNNING))
 
441
        {
 
442
            state = STOPPED;
 
443
            start(value.get(), 0);
 
444
        }
 
445
        else state &= ~SLEEPING;
 
446
    }
 
447
}