]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blame - sim/mips/dv-tx3904tmr.c
Copyright year update in most files of the GDB Project.
[thirdparty/binutils-gdb.git] / sim / mips / dv-tx3904tmr.c
CommitLineData
c906108c
SS
1/* This file is part of the program GDB, the GNU debugger.
2
c5a57081 3 Copyright (C) 1998, 2007-2012 Free Software Foundation, Inc.
c906108c
SS
4 Contributed by Cygnus Solutions.
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
4744ac1b 8 the Free Software Foundation; either version 3 of the License, or
c906108c 9 (at your option) any later version.
4744ac1b 10
c906108c
SS
11 This program 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
14 GNU General Public License for more details.
4744ac1b 15
c906108c 16 You should have received a copy of the GNU General Public License
4744ac1b 17 along with this program. If not, see <http://www.gnu.org/licenses/>.
c906108c
SS
18
19 */
20
21
22#include "sim-main.h"
23#include "hw-main.h"
24
25
26/* DEVICE
27
28
29 tx3904tmr - tx3904 timer
30
31
32 DESCRIPTION
33
34
35 Implements one tx3904 timer/counter described in the tx3904
36 user guide. Three instances are required for TMR0, TMR1, and
37 TMR3 within the tx3904, at different base addresses.
38
39 Both internal and system clocks are synthesized as divided versions
40 of the simulator clock.
41
42 There is no support for:
43 - edge sensitivity of external clock
44 - different mode restrictions for TMR0..2
45 - level interrupts (interrupts are treated as events that occur at edges)
46
47
48
49 PROPERTIES
50
51
52 reg <base> <length>
53
54 Base of TMR control register bank. <length> must equal 0x100.
55 Register offsets: 0: TCR: timer control register
56 4: TISR: timer interrupt status register
57 8: CPRA: compare register A
58 12: CPRB: compare register B
59 16: ITMR: interval timer mode register
60 32: CCDR: divider register
61 48: PMGR: pulse generator mode register
62 64: WTMR: watchdog timer mode register
63 240: TRR: timer read register
64
65
66 clock <ticks>
67
68 Rate of timer clock signal. This number is the number of simulator
69 ticks per clock signal tick. Default 1.
70
71
72 ext <ticks>
73
74 Rate of "external input clock signal", the other clock input of the
75 timer. It uses the same scale as above. Default 100.
76
77
78
79 PORTS
80
81
82 int (output)
83
84 Interrupt port. An event is generated when a timer interrupt
85 occurs.
86
87
88 ff (output)
89
90 Flip-flop output, corresponds to the TMFFOUT port. An event is
91 generated when flip-flop changes value. The integer associated
92 with the event is 1/0 according to flip-flop value.
93
94
95 reset (input)
96
97 Reset port.
98
99 */
100
101
102
103/* static functions */
104
105static void deliver_tx3904tmr_tick (struct hw *me, void *data);
106
107
108/* register numbers; each is one word long */
109enum
110{
111 TCR_REG = 0,
112 TISR_REG = 1,
113 CPRA_REG = 2,
114 CPRB_REG = 3,
115 ITMR_REG = 4,
116 CCDR_REG = 8,
117 PMGR_REG = 12,
118 WTMR_REG = 16,
119 TRR_REG = 60
120};
121
122
123
124/* port ID's */
125
126enum
127 {
128 RESET_PORT,
129 INT_PORT,
130 FF_PORT
131};
132
133
134static const struct hw_port_descriptor tx3904tmr_ports[] =
135{
136 { "int", INT_PORT, 0, output_port, },
137 { "ff", FF_PORT, 0, output_port, },
138 { "reset", RESET_PORT, 0, input_port, },
139 { NULL, },
140};
141
142
143
144/* The timer/counter register internal state. Note that we store
145 state using the control register images, in host endian order. */
146
147struct tx3904tmr {
148 address_word base_address; /* control register base */
149 unsigned_4 clock_ticks, ext_ticks; /* clock frequencies */
150 signed_8 last_ticks; /* time at last deliver_*_tick call */
151 signed_8 roundoff_ticks; /* sim ticks unprocessed during last tick call */
152 int ff; /* pulse generator flip-flop value: 1/0 */
153 struct hw_event* event; /* last scheduled event */
154
155 unsigned_4 tcr;
156#define GET_TCR_TCE(c) (((c)->tcr & 0x80) >> 7)
157#define GET_TCR_CCDE(c) (((c)->tcr & 0x40) >> 6)
158#define GET_TCR_CRE(c) (((c)->tcr & 0x20) >> 5)
159#define GET_TCR_CCS(c) (((c)->tcr & 0x04) >> 2)
160#define GET_TCR_TMODE(c) (((c)->tcr & 0x03) >> 0)
161 unsigned_4 tisr;
162#define SET_TISR_TWIS(c) ((c)->tisr |= 0x08)
163#define SET_TISR_TPIBS(c) ((c)->tisr |= 0x04)
164#define SET_TISR_TPIAS(c) ((c)->tisr |= 0x02)
165#define SET_TISR_TIIS(c) ((c)->tisr |= 0x01)
166 unsigned_4 cpra;
167 unsigned_4 cprb;
168 unsigned_4 itmr;
169#define GET_ITMR_TIIE(c) (((c)->itmr & 0x8000) >> 15)
170#define SET_ITMR_TIIE(c,v) BLIT32((c)->itmr, 15, (v) ? 1 : 0)
171#define GET_ITMR_TZCE(c) (((c)->itmr & 0x0001) >> 0)
172#define SET_ITMR_TZCE(c,v) BLIT32((c)->itmr, 0, (v) ? 1 : 0)
173 unsigned_4 ccdr;
174#define GET_CCDR_CDR(c) (((c)->ccdr & 0x07) >> 0)
175 unsigned_4 pmgr;
176#define GET_PMGR_TPIBE(c) (((c)->pmgr & 0x8000) >> 15)
177#define SET_PMGR_TPIBE(c,v) BLIT32((c)->pmgr, 15, (v) ? 1 : 0)
178#define GET_PMGR_TPIAE(c) (((c)->pmgr & 0x4000) >> 14)
179#define SET_PMGR_TPIAE(c,v) BLIT32((c)->pmgr, 14, (v) ? 1 : 0)
180#define GET_PMGR_FFI(c) (((c)->pmgr & 0x0001) >> 0)
181#define SET_PMGR_FFI(c,v) BLIT32((c)->pmgr, 0, (v) ? 1 : 0)
182 unsigned_4 wtmr;
183#define GET_WTMR_TWIE(c) (((c)->wtmr & 0x8000) >> 15)
184#define SET_WTMR_TWIE(c,v) BLIT32((c)->wtmr, 15, (v) ? 1 : 0)
185#define GET_WTMR_WDIS(c) (((c)->wtmr & 0x0080) >> 7)
186#define SET_WTMR_WDIS(c,v) BLIT32((c)->wtmr, 7, (v) ? 1 : 0)
187#define GET_WTMR_TWC(c) (((c)->wtmr & 0x0001) >> 0)
188#define SET_WTMR_TWC(c,v) BLIT32((c)->wtmr, 0, (v) ? 1 : 0)
189 unsigned_4 trr;
190};
191
192
193
194/* Finish off the partially created hw device. Attach our local
195 callbacks. Wire up our port names etc */
196
197static hw_io_read_buffer_method tx3904tmr_io_read_buffer;
198static hw_io_write_buffer_method tx3904tmr_io_write_buffer;
199static hw_port_event_method tx3904tmr_port_event;
200
201static void
202attach_tx3904tmr_regs (struct hw *me,
203 struct tx3904tmr *controller)
204{
205 unsigned_word attach_address;
206 int attach_space;
207 unsigned attach_size;
208 reg_property_spec reg;
209
210 if (hw_find_property (me, "reg") == NULL)
211 hw_abort (me, "Missing \"reg\" property");
212
213 if (!hw_find_reg_array_property (me, "reg", 0, &reg))
214 hw_abort (me, "\"reg\" property must contain one addr/size entry");
215
216 hw_unit_address_to_attach_address (hw_parent (me),
217 &reg.address,
218 &attach_space,
219 &attach_address,
220 me);
221 hw_unit_size_to_attach_size (hw_parent (me),
222 &reg.size,
223 &attach_size, me);
224
225 hw_attach_address (hw_parent (me), 0,
226 attach_space, attach_address, attach_size,
227 me);
228
229 if(hw_find_property(me, "clock") != NULL)
230 controller->clock_ticks = (unsigned_4) hw_find_integer_property(me, "clock");
231
232 if(hw_find_property(me, "ext") != NULL)
233 controller->ext_ticks = (unsigned_4) hw_find_integer_property(me, "ext");
234
235 controller->base_address = attach_address;
236}
237
238
239static void
240tx3904tmr_finish (struct hw *me)
241{
242 struct tx3904tmr *controller;
243
244 controller = HW_ZALLOC (me, struct tx3904tmr);
245 set_hw_data (me, controller);
246 set_hw_io_read_buffer (me, tx3904tmr_io_read_buffer);
247 set_hw_io_write_buffer (me, tx3904tmr_io_write_buffer);
248 set_hw_ports (me, tx3904tmr_ports);
249 set_hw_port_event (me, tx3904tmr_port_event);
250
251 /* Preset clock dividers */
252 controller->clock_ticks = 1;
253 controller->ext_ticks = 100;
254
255 /* Attach ourself to our parent bus */
256 attach_tx3904tmr_regs (me, controller);
257
258 /* Initialize to reset state */
259 controller->tcr =
260 controller->itmr =
261 controller->ccdr =
262 controller->pmgr =
263 controller->wtmr =
264 controller->tisr =
265 controller->trr = 0;
266 controller->cpra = controller->cprb = 0x00FFFFFF;
267 controller->ff = 0;
268 controller->last_ticks = controller->roundoff_ticks = 0;
269 controller->event = NULL;
270}
271
272
273
274/* An event arrives on an interrupt port */
275
276static void
277tx3904tmr_port_event (struct hw *me,
278 int my_port,
279 struct hw *source,
280 int source_port,
281 int level)
282{
283 struct tx3904tmr *controller = hw_data (me);
284
285 switch (my_port)
286 {
287 case RESET_PORT:
288 {
289 HW_TRACE ((me, "reset"));
290
291 /* preset flip-flop to FFI value */
292 controller->ff = GET_PMGR_FFI(controller);
293
294 controller->tcr =
295 controller->itmr =
296 controller->ccdr =
297 controller->pmgr =
298 controller->wtmr =
299 controller->tisr =
300 controller->trr = 0;
301 controller->cpra = controller->cprb = 0x00FFFFFF;
302 controller->last_ticks = controller->roundoff_ticks = 0;
303 if(controller->event != NULL)
304 hw_event_queue_deschedule(me, controller->event);
305 controller->event = NULL;
306 break;
307 }
308
309 default:
310 hw_abort (me, "Event on unknown port %d", my_port);
311 break;
312 }
313}
314
315
316/* generic read/write */
317
318static unsigned
319tx3904tmr_io_read_buffer (struct hw *me,
320 void *dest,
321 int space,
322 unsigned_word base,
323 unsigned nr_bytes)
324{
325 struct tx3904tmr *controller = hw_data (me);
326 unsigned byte;
327
328 HW_TRACE ((me, "read 0x%08lx %d", (long) base, (int) nr_bytes));
329 for (byte = 0; byte < nr_bytes; byte++)
330 {
331 address_word address = base + byte;
332 int reg_number = (address - controller->base_address) / 4;
333 int reg_offset = 3 - (address - controller->base_address) % 4;
334 unsigned_4 register_value; /* in target byte order */
335
336 /* fill in entire register_value word */
337 switch (reg_number)
338 {
339 case TCR_REG: register_value = controller->tcr; break;
340 case TISR_REG: register_value = controller->tisr; break;
341 case CPRA_REG: register_value = controller->cpra; break;
342 case CPRB_REG: register_value = controller->cprb; break;
343 case ITMR_REG: register_value = controller->itmr; break;
344 case CCDR_REG: register_value = controller->ccdr; break;
345 case PMGR_REG: register_value = controller->pmgr; break;
346 case WTMR_REG: register_value = controller->wtmr; break;
347 case TRR_REG: register_value = controller->trr; break;
348 default: register_value = 0;
349 }
350
351 /* write requested byte out */
352 memcpy ((char*) dest + byte, ((char*)& register_value)+reg_offset, 1);
353 }
354
355 return nr_bytes;
356}
357
358
359
360static unsigned
361tx3904tmr_io_write_buffer (struct hw *me,
362 const void *source,
363 int space,
364 unsigned_word base,
365 unsigned nr_bytes)
366{
367 struct tx3904tmr *controller = hw_data (me);
368 unsigned byte;
369
370 HW_TRACE ((me, "write 0x%08lx %d", (long) base, (int) nr_bytes));
371 for (byte = 0; byte < nr_bytes; byte++)
372 {
373 address_word address = base + byte;
374 unsigned_1 write_byte = ((const char*) source)[byte];
375 int reg_number = (address - controller->base_address) / 4;
376 int reg_offset = 3 - (address - controller->base_address) % 4;
377
378 /* fill in entire register_value word */
379 switch (reg_number)
380 {
381 case TCR_REG:
382 if(reg_offset == 0) /* first byte */
383 {
384 /* update register, but mask out NOP bits */
385 controller->tcr = (unsigned_4) (write_byte & 0xef);
386
387 /* Reset counter value if timer suspended and CRE is set. */
388 if(GET_TCR_TCE(controller) == 0 &&
389 GET_TCR_CRE(controller) == 1)
390 controller->trr = 0;
391 }
392 /* HW_TRACE ((me, "tcr: %08lx", (long) controller->tcr)); */
393 break;
394
395 case ITMR_REG:
396 if(reg_offset == 1) /* second byte */
397 {
398 SET_ITMR_TIIE(controller, write_byte & 0x80);
399 }
400 else if(reg_offset == 0) /* first byte */
401 {
402 SET_ITMR_TZCE(controller, write_byte & 0x01);
403 }
404 /* HW_TRACE ((me, "itmr: %08lx", (long) controller->itmr)); */
405 break;
406
407 case CCDR_REG:
408 if(reg_offset == 0) /* first byte */
409 {
410 controller->ccdr = write_byte & 0x07;
411 }
412 /* HW_TRACE ((me, "ccdr: %08lx", (long) controller->ccdr)); */
413 break;
414
415 case PMGR_REG:
416 if(reg_offset == 1) /* second byte */
417 {
418 SET_PMGR_TPIBE(controller, write_byte & 0x80);
419 SET_PMGR_TPIAE(controller, write_byte & 0x40);
420 }
421 else if(reg_offset == 0) /* first byte */
422 {
423 SET_PMGR_FFI(controller, write_byte & 0x01);
424 }
425 /* HW_TRACE ((me, "pmgr: %08lx", (long) controller->pmgr)); */
426 break;
427
428 case WTMR_REG:
429 if(reg_offset == 1) /* second byte */
430 {
431 SET_WTMR_TWIE(controller, write_byte & 0x80);
432 }
433 else if(reg_offset == 0) /* first byte */
434 {
435 SET_WTMR_WDIS(controller, write_byte & 0x80);
436 SET_WTMR_TWC(controller, write_byte & 0x01);
437 }
438 /* HW_TRACE ((me, "wtmr: %08lx", (long) controller->wtmr)); */
439 break;
440
441 case TISR_REG:
442 if(reg_offset == 0) /* first byte */
443 {
444 /* All bits must be zero in given byte, according to
445 spec. */
446
447 /* Send an "interrupt off" event on the interrupt port */
448 if(controller->tisr != 0) /* any interrupts active? */
449 {
450 hw_port_event(me, INT_PORT, 0);
451 }
452
453 /* clear interrupt status register */
454 controller->tisr = 0;
455 }
456 /* HW_TRACE ((me, "tisr: %08lx", (long) controller->tisr)); */
457 break;
458
459 case CPRA_REG:
460 if(reg_offset < 3) /* first, second, or third byte */
461 {
462 MBLIT32(controller->cpra, (reg_offset*8)+7, (reg_offset*8), write_byte);
463 }
464 /* HW_TRACE ((me, "cpra: %08lx", (long) controller->cpra)); */
465 break;
466
467 case CPRB_REG:
468 if(reg_offset < 3) /* first, second, or third byte */
469 {
470 MBLIT32(controller->cprb, (reg_offset*8)+7, (reg_offset*8), write_byte);
471 }
472 /* HW_TRACE ((me, "cprb: %08lx", (long) controller->cprb)); */
473 break;
474
475 default:
476 HW_TRACE ((me, "write to illegal register %d", reg_number));
477 }
478 } /* loop over bytes */
479
480 /* Schedule a timer event in near future, so we can increment or
481 stop the counter, to respond to register updates. */
482 hw_event_queue_schedule(me, 1, deliver_tx3904tmr_tick, NULL);
483
484 return nr_bytes;
485}
486
487
488
489/* Deliver a clock tick to the counter. */
490static void
491deliver_tx3904tmr_tick (struct hw *me,
492 void *data)
493{
494 struct tx3904tmr *controller = hw_data (me);
495 SIM_DESC sd = hw_system (me);
496 signed_8 this_ticks = sim_events_time(sd);
497
498 signed_8 warp;
499 signed_8 divisor;
500 signed_8 quotient, remainder;
501
502 /* compute simulation ticks between last tick and this tick */
503 if(controller->last_ticks != 0)
504 warp = this_ticks - controller->last_ticks + controller->roundoff_ticks;
505 else
506 {
507 controller->last_ticks = this_ticks; /* initialize */
508 warp = controller->roundoff_ticks;
509 }
510
511 if(controller->event != NULL)
512 hw_event_queue_deschedule(me, controller->event);
513 controller->event = NULL;
514
515 /* Check whether the timer ticking is enabled at this moment. This
516 largely a function of the TCE bit, but is also slightly
517 mode-dependent. */
518 switch((int) GET_TCR_TMODE(controller))
519 {
520 case 0: /* interval */
521 /* do not advance counter if TCE = 0 or if holding at count = CPRA */
522 if(GET_TCR_TCE(controller) == 0 ||
523 controller->trr == controller->cpra)
524 return;
525 break;
526
527 case 1: /* pulse generator */
528 /* do not advance counter if TCE = 0 */
529 if(GET_TCR_TCE(controller) == 0)
530 return;
531 break;
532
533 case 2: /* watchdog */
534 /* do not advance counter if TCE = 0 and WDIS = 1 */
535 if(GET_TCR_TCE(controller) == 0 &&
536 GET_WTMR_WDIS(controller) == 1)
537 return;
538 break;
539
540 case 3: /* disabled */
541 /* regardless of TCE, do not advance counter */
542 return;
543 }
544
545 /* In any of the above cases that return, a subsequent register
546 write will be needed to restart the timer. A tick event is
547 scheduled by any register write, so it is more efficient not to
548 reschedule dummy events here. */
549
550
551 /* find appropriate divisor etc. */
552 if(GET_TCR_CCS(controller) == 0) /* internal system clock */
553 {
554 /* apply internal clock divider */
555 if(GET_TCR_CCDE(controller)) /* divisor circuit enabled? */
556 divisor = controller->clock_ticks * (1 << (1 + GET_CCDR_CDR(controller)));
557 else
558 divisor = controller->clock_ticks;
559 }
560 else
561 {
562 divisor = controller->ext_ticks;
563 }
564
565 /* how many times to increase counter? */
566 quotient = warp / divisor;
567 remainder = warp % divisor;
568
569 /* NOTE: If the event rescheduling code works properly, the quotient
570 should never be larger than 1. That is, we should receive events
571 here at least as frequently as the simulated counter is supposed
572 to decrement. So the remainder (-> roundoff_ticks) will slowly
573 accumulate, with the quotient == 0. Once in a while, quotient
574 will equal 1. */
575
576 controller->roundoff_ticks = remainder;
577 controller->last_ticks = this_ticks;
578 while(quotient > 0) /* Is it time to increment counter? */
579 {
580 /* next 24-bit counter value */
581 unsigned_4 next_trr = (controller->trr + 1) % (1 << 24);
582 quotient --;
583
584 switch((int) GET_TCR_TMODE(controller))
585 {
586 case 0: /* interval timer mode */
587 {
588 /* Current or next counter value matches CPRA value? The
589 first case covers counter holding at maximum before
590 reset. The second case covers normal counting
591 behavior. */
592 if(controller->trr == controller->cpra ||
593 next_trr == controller->cpra)
594 {
595 /* likely hold CPRA value */
596 if(controller->trr == controller->cpra)
597 next_trr = controller->cpra;
598
599 SET_TISR_TIIS(controller);
600
601 /* Signal an interrupt if it is enabled with TIIE,
602 and if we just arrived at CPRA. Don't repeatedly
603 interrupt if holding due to TZCE=0 */
604 if(GET_ITMR_TIIE(controller) &&
605 next_trr != controller->trr)
606 {
607 hw_port_event(me, INT_PORT, 1);
608 }
609
610 /* Reset counter? */
611 if(GET_ITMR_TZCE(controller))
612 {
613 next_trr = 0;
614 }
615 }
616 }
617 break;
618
619 case 1: /* pulse generator mode */
620 {
621 /* first trip point */
622 if(next_trr == controller->cpra)
623 {
624 /* flip flip-flop & report */
625 controller->ff ^= 1;
626 hw_port_event(me, FF_PORT, controller->ff);
627 SET_TISR_TPIAS(controller);
628
629 /* signal interrupt */
630 if(GET_PMGR_TPIAE(controller))
631 {
632 hw_port_event(me, INT_PORT, 1);
633 }
634
635 }
636 /* second trip point */
637 else if(next_trr == controller->cprb)
638 {
639 /* flip flip-flop & report */
640 controller->ff ^= 1;
641 hw_port_event(me, FF_PORT, controller->ff);
642 SET_TISR_TPIBS(controller);
643
644 /* signal interrupt */
645 if(GET_PMGR_TPIBE(controller))
646 {
647 hw_port_event(me, INT_PORT, 1);
648 }
649
650 /* clear counter */
651 next_trr = 0;
652 }
653 }
654 break;
655
656 case 2: /* watchdog timer mode */
657 {
658 /* watchdog timer expiry */
659 if(next_trr == controller->cpra)
660 {
661 SET_TISR_TWIS(controller);
662
663 /* signal interrupt */
664 if(GET_WTMR_TWIE(controller))
665 {
666 hw_port_event(me, INT_PORT, 1);
667 }
668
669 /* clear counter */
670 next_trr = 0;
671 }
672 }
673 break;
674
675 case 3: /* disabled */
676 default:
0275de4e 677 break;
c906108c
SS
678 }
679
680 /* update counter and report */
681 controller->trr = next_trr;
682 /* HW_TRACE ((me, "counter trr %ld tisr %lx",
683 (long) controller->trr, (long) controller->tisr)); */
684 } /* end quotient loop */
685
686 /* Reschedule a timer event in near future, so we can increment the
687 counter again. Set the event about 75% of divisor time away, so
688 we will experience roughly 1.3 events per counter increment. */
689 controller->event = hw_event_queue_schedule(me, divisor*3/4, deliver_tx3904tmr_tick, NULL);
690}
691
692
693
694
695const struct hw_descriptor dv_tx3904tmr_descriptor[] = {
696 { "tx3904tmr", tx3904tmr_finish, },
697 { NULL },
698};