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