aboutsummaryrefslogtreecommitdiff
path: root/sim
diff options
context:
space:
mode:
authorFrank Ch. Eigler <fche@redhat.com>1998-06-04 12:43:45 +0000
committerFrank Ch. Eigler <fche@redhat.com>1998-06-04 12:43:45 +0000
commitda040f2a6c7a5dfa3c2bacecc97128ac4477d904 (patch)
treed277d4307eecfd810b83c298ab6c1d9fdef7419c /sim
parent0e797366efd0c7aad9acddcaeac4a55a0fd8bfb7 (diff)
downloadfsf-binutils-gdb-da040f2a6c7a5dfa3c2bacecc97128ac4477d904.zip
fsf-binutils-gdb-da040f2a6c7a5dfa3c2bacecc97128ac4477d904.tar.gz
fsf-binutils-gdb-da040f2a6c7a5dfa3c2bacecc97128ac4477d904.tar.bz2
* Early check-in of tx3904 timer sim implementation for ECC.
It is not yet properly tested. Thu Jun 4 15:37:33 1998 Frank Ch. Eigler <fche@cygnus.com> * dv-tx3904tmr.c: New file - implements tx3904 timer. * dv-tx3904{irc,cpu}.c: Mild reformatting. * configure.in: Include tx3904tmr in hw_device list. * configure: Rebuilt. * interp.c (sim_open): Instantiate three timer instances. Fix address typo of tx3904irc instance.
Diffstat (limited to 'sim')
-rw-r--r--sim/mips/.Sanitize2
-rw-r--r--sim/mips/ChangeLog11
-rwxr-xr-xsim/mips/configure2
-rw-r--r--sim/mips/configure.in2
-rw-r--r--sim/mips/dv-tx3904cpu.c10
-rw-r--r--sim/mips/dv-tx3904irc.c46
-rw-r--r--sim/mips/dv-tx3904tmr.c681
-rw-r--r--sim/mips/interp.c8
8 files changed, 734 insertions, 28 deletions
diff --git a/sim/mips/.Sanitize b/sim/mips/.Sanitize
index 135288d..c8a8563 100644
--- a/sim/mips/.Sanitize
+++ b/sim/mips/.Sanitize
@@ -31,7 +31,7 @@ else
lose_these_too="${vr4320_files} ${lose_these_too}"
fi
-tx3904_files="dv-tx3904cpu.c dv-tx3904irc.c"
+tx3904_files="dv-tx3904cpu.c dv-tx3904irc.c dv-tx3904tmr.c"
if ( echo $* | grep keep\-tx3904 > /dev/null ) ; then
keep_these_too="${tx3904_files} ${keep_these_too}"
else
diff --git a/sim/mips/ChangeLog b/sim/mips/ChangeLog
index 9a16c1d..bd07b9c 100644
--- a/sim/mips/ChangeLog
+++ b/sim/mips/ChangeLog
@@ -1,3 +1,14 @@
+start-sanitize-tx3904
+Thu Jun 4 15:37:33 1998 Frank Ch. Eigler <fche@cygnus.com>
+
+ * dv-tx3904tmr.c: New file - implements tx3904 timer.
+ * dv-tx3904{irc,cpu}.c: Mild reformatting.
+ * configure.in: Include tx3904tmr in hw_device list.
+ * configure: Rebuilt.
+ * interp.c (sim_open): Instantiate three timer instances.
+ Fix address typo of tx3904irc instance.
+
+end-sanitize-tx3904
start-sanitize-r5900
Thu Jun 4 16:47:27 1998 Andrew Cagney <cagney@b1.cygnus.com>
diff --git a/sim/mips/configure b/sim/mips/configure
index db212ad..fde6b62 100755
--- a/sim/mips/configure
+++ b/sim/mips/configure
@@ -3840,7 +3840,7 @@ esac
#
hw_enabled=no
case "${target}" in
- mips*tx39*) hw_enabled=yes ; hw_extra_devices="tx3904cpu tx3904irc" ;;
+ mips*tx39*) hw_enabled=yes ; hw_extra_devices="tx3904cpu tx3904irc tx3904tmr" ;;
*) ;;
esac
diff --git a/sim/mips/configure.in b/sim/mips/configure.in
index 9bd1a84..a32b57b 100644
--- a/sim/mips/configure.in
+++ b/sim/mips/configure.in
@@ -245,7 +245,7 @@ AC_SUBST(mips_extra_objs)
#
hw_enabled=no
case "${target}" in
- mips*tx39*) hw_enabled=yes ; hw_extra_devices="tx3904cpu tx3904irc" ;;
+ mips*tx39*) hw_enabled=yes ; hw_extra_devices="tx3904cpu tx3904irc tx3904tmr" ;;
*) ;;
esac
SIM_AC_OPTION_HARDWARE($hw_enabled,$hw_devices,$hw_extra_devices)
diff --git a/sim/mips/dv-tx3904cpu.c b/sim/mips/dv-tx3904cpu.c
index f756f22..d043253 100644
--- a/sim/mips/dv-tx3904cpu.c
+++ b/sim/mips/dv-tx3904cpu.c
@@ -21,7 +21,7 @@
#include "sim-main.h"
-#include "hw-base.h"
+#include "hw-main.h"
/* DEVICE
@@ -109,7 +109,7 @@ static const struct hw_port_descriptor tx3904cpu_ports[] = {
/* Finish off the partially created hw device. Attach our local
callbacks. Wire up our port names etc */
-static hw_port_event_callback tx3904cpu_port_event;
+static hw_port_event_method tx3904cpu_port_event;
@@ -138,8 +138,8 @@ deliver_tx3904cpu_interrupt (struct hw *me,
void *data)
{
struct tx3904cpu *controller = hw_data (me);
- SIM_DESC simulator = hw_system (me);
- sim_cpu *cpu = STATE_CPU (simulator, 0); /* NB: fix CPU 0. */
+ SIM_DESC sd = hw_system (me);
+ sim_cpu *cpu = STATE_CPU (sd, 0); /* NB: fix CPU 0. */
address_word cia = CIA_GET (cpu);
#define CPU cpu
@@ -224,7 +224,7 @@ tx3904cpu_port_event (struct hw *me,
}
-const struct hw_device_descriptor dv_tx3904cpu_descriptor[] = {
+const struct hw_descriptor dv_tx3904cpu_descriptor[] = {
{ "tx3904cpu", tx3904cpu_finish, },
{ NULL },
};
diff --git a/sim/mips/dv-tx3904irc.c b/sim/mips/dv-tx3904irc.c
index d8064d3..f0d3260 100644
--- a/sim/mips/dv-tx3904irc.c
+++ b/sim/mips/dv-tx3904irc.c
@@ -21,7 +21,7 @@
#include "sim-main.h"
-#include "hw-base.h"
+#include "hw-main.h"
/* DEVICE
@@ -35,7 +35,10 @@
Implements the tx3904 interrupt controller described in the tx3904
user guide. It does not include the interrupt detection circuit
- that preprocesses the eight external interrupts.
+ that preprocesses the eight external interrupts, so assumes that
+ each event on an input interrupt port signals a new interrupt.
+ That is, it implements edge- rather than level-triggered
+ interrupts.
PROPERTIES
@@ -89,9 +92,24 @@
+
+
+/* register numbers; each is one word long */
+enum
+{
+ ISR_REG = 0,
+ IMR_REG = 1,
+ ILR0_REG = 4,
+ ILR1_REG = 5,
+ ILR2_REG = 6,
+ ILR3_REG = 7,
+};
+
+
/* port ID's */
-enum {
+enum
+{
/* inputs, ordered to correspond to interrupt sources 0..15 */
INT1_PORT = 0, INT2_PORT, INT3_PORT, INT4_PORT, INT5_PORT, INT6_PORT, INT7_PORT,
DMAC3_PORT, DMAC2_PORT, DMAC1_PORT, DMAC0_PORT, SIO0_PORT, SIO1_PORT,
@@ -108,18 +126,6 @@ enum {
};
-/* register numbers; each is one word long */
-enum {
- ISR_REG = 0,
- IMR_REG = 1,
- ILR0_REG = 4,
- ILR1_REG = 5,
- ILR2_REG = 6,
- ILR3_REG = 7,
-};
-
-
-
static const struct hw_port_descriptor tx3904irc_ports[] = {
/* interrupt output */
@@ -178,9 +184,9 @@ struct tx3904irc {
/* Finish off the partially created hw device. Attach our local
callbacks. Wire up our port names etc */
-static hw_io_read_buffer_callback tx3904irc_io_read_buffer;
-static hw_io_write_buffer_callback tx3904irc_io_write_buffer;
-static hw_port_event_callback tx3904irc_port_event;
+static hw_io_read_buffer_method tx3904irc_io_read_buffer;
+static hw_io_write_buffer_method tx3904irc_io_write_buffer;
+static hw_port_event_method tx3904irc_port_event;
static void
attach_tx3904irc_regs (struct hw *me,
@@ -251,6 +257,8 @@ tx3904irc_port_event (struct hw *me,
{
struct tx3904irc *controller = hw_data (me);
+ /* Ignore level - use only edge-triggered interrupts */
+
switch (my_port)
{
case INT0_PORT:
@@ -385,7 +393,7 @@ tx3904irc_io_write_buffer (struct hw *me,
}
-const struct hw_device_descriptor dv_tx3904irc_descriptor[] = {
+const struct hw_descriptor dv_tx3904irc_descriptor[] = {
{ "tx3904irc", tx3904irc_finish, },
{ NULL },
};
diff --git a/sim/mips/dv-tx3904tmr.c b/sim/mips/dv-tx3904tmr.c
new file mode 100644
index 0000000..b101421
--- /dev/null
+++ b/sim/mips/dv-tx3904tmr.c
@@ -0,0 +1,681 @@
+/* This file is part of the program GDB, the GNU debugger.
+
+ Copyright (C) 1998 Free Software Foundation, Inc.
+ Contributed by Cygnus Solutions.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+
+#include "sim-main.h"
+#include "hw-main.h"
+
+
+/* DEVICE
+
+
+ tx3904tmr - tx3904 timer
+
+
+ DESCRIPTION
+
+
+ Implements one tx3904 timer/counter described in the tx3904
+ user guide. Three instances are required for TMR0, TMR1, and
+ TMR3 within the tx3904, at different base addresses.
+
+ Both internal and system clocks are synthesized as divided versions
+ of the simulator clock.
+
+ There is no support for:
+ - edge sensitivity of external clock
+ - different mode restrictions for TMR0..2
+ - level interrupts (interrupts are treated as events that occur at edges)
+
+
+
+ PROPERTIES
+
+
+ reg <base> <length>
+
+ Base of TMR control register bank. <length> must equal 0x100.
+ Register offsets: 0: TCR: timer control register
+ 4: TISR: timer interrupt status register
+ 8: CPRA: compare register A
+ 12: CPRB: compare register B
+ 16: ITMR: interval timer mode register
+ 32: CCDR: divider register
+ 48: PMGR: pulse generator mode register
+ 64: WTMR: watchdog timer mode register
+ 240: TRR: timer read register
+
+
+ clock <ticks>
+
+ Rate of timer clock signal. This number is the number of simulator
+ ticks per clock signal tick. Default 1.
+
+
+ ext <ticks>
+
+ Rate of "external input clock signal", the other clock input of the
+ timer. It uses the same scale as above. Default 100.
+
+
+
+ PORTS
+
+
+ int (output)
+
+ Interrupt port. An event is generated when a timer interrupt
+ occurs.
+
+
+ ff (output)
+
+ Flip-flop output, corresponds to the TMFFOUT port. An event is
+ generated when flip-flop changes value. The integer associated
+ with the event is 1/0 according to flip-flop value.
+
+
+ reset (input)
+
+ Reset port.
+
+ */
+
+
+
+/* static functions */
+
+static void deliver_tx3904tmr_tick (struct hw *me, void *data);
+
+
+/* register numbers; each is one word long */
+enum
+{
+ TCR_REG = 0,
+ TISR_REG = 1,
+ CPRA_REG = 2,
+ CPRB_REG = 3,
+ ITMR_REG = 4,
+ CCDR_REG = 8,
+ PMGR_REG = 12,
+ WTMR_REG = 16,
+ TRR_REG = 60
+};
+
+
+
+/* port ID's */
+
+enum
+ {
+ RESET_PORT,
+ INT_PORT,
+ FF_PORT
+};
+
+
+static const struct hw_port_descriptor tx3904tmr_ports[] =
+{
+ { "int", INT_PORT, 0, output_port, },
+ { "ff", FF_PORT, 0, output_port, },
+ { "reset", RESET_PORT, 0, input_port, },
+ { NULL, },
+};
+
+
+
+/* The timer/counter register internal state. Note that we store
+ state using the control register images, in host endian order. */
+
+struct tx3904tmr {
+ address_word base_address; /* control register base */
+ unsigned_4 clock_ticks, ext_ticks; /* clock frequencies */
+ signed_8 last_ticks; /* time at last deliver_*_tick call */
+ signed_8 roundoff_ticks; /* sim ticks unprocessed during last tick call */
+ int ff; /* pulse generator flip-flop value: 1/0 */
+
+ unsigned_4 tcr;
+#define GET_TCR_TCE(c) (((c)->tcr & 0x80) >> 7)
+#define GET_TCR_CCDE(c) (((c)->tcr & 0x40) >> 6)
+#define GET_TCR_CRE(c) (((c)->tcr & 0x20) >> 5)
+#define GET_TCR_CCS(c) (((c)->tcr & 0x04) >> 2)
+#define GET_TCR_TMODE(c) (((c)->tcr & 0x03) >> 0)
+ unsigned_4 tisr;
+#define SET_TISR_TWIS(c) ((c)->tisr |= 0x08)
+#define SET_TISR_TPIBS(c) ((c)->tisr |= 0x04)
+#define SET_TISR_TPIAS(c) ((c)->tisr |= 0x02)
+#define SET_TISR_TIIS(c) ((c)->tisr |= 0x01)
+ unsigned_4 cpra;
+ unsigned_4 cprb;
+ unsigned_4 itmr;
+#define GET_ITMR_TIIE(c) (((c)->itmr & 0x8000) >> 15)
+#define SET_ITMR_TIIE(c,v) BLIT32((c)->itmr, 15, (v) ? 1 : 0)
+#define GET_ITMR_TZCE(c) (((c)->itmr & 0x0001) >> 0)
+#define SET_ITMR_TZCE(c,v) BLIT32((c)->itmr, 0, (v) ? 1 : 0)
+ unsigned_4 ccdr;
+#define GET_CCDR_CDR(c) (((c)->ccdr & 0x07) >> 0)
+ unsigned_4 pmgr;
+#define GET_PMGR_TPIBE(c) (((c)->pmgr & 0x8000) >> 15)
+#define SET_PMGR_TPIBE(c,v) BLIT32((c)->pmgr, 15, (v) ? 1 : 0)
+#define GET_PMGR_TPIAE(c) (((c)->pmgr & 0x4000) >> 14)
+#define SET_PMGR_TPIAE(c,v) BLIT32((c)->pmgr, 14, (v) ? 1 : 0)
+#define GET_PMGR_FFI(c) (((c)->pmgr & 0x0001) >> 0)
+#define SET_PMGR_FFI(c,v) BLIT32((c)->pmgr, 0, (v) ? 1 : 0)
+ unsigned_4 wtmr;
+#define GET_WTMR_TWIE(c) (((c)->wtmr & 0x8000) >> 15)
+#define SET_WTMR_TWIE(c,v) BLIT32((c)->wtmr, 15, (v) ? 1 : 0)
+#define GET_WTMR_WDIS(c) (((c)->wtmr & 0x0080) >> 7)
+#define SET_WTMR_WDIS(c,v) BLIT32((c)->wtmr, 7, (v) ? 1 : 0)
+#define GET_WTMR_TWC(c) (((c)->wtmr & 0x0001) >> 0)
+#define SET_WTMR_TWC(c,v) BLIT32((c)->wtmr, 0, (v) ? 1 : 0)
+ unsigned_4 trr;
+};
+
+
+
+/* Finish off the partially created hw device. Attach our local
+ callbacks. Wire up our port names etc */
+
+static hw_io_read_buffer_method tx3904tmr_io_read_buffer;
+static hw_io_write_buffer_method tx3904tmr_io_write_buffer;
+static hw_port_event_method tx3904tmr_port_event;
+
+static void
+attach_tx3904tmr_regs (struct hw *me,
+ struct tx3904tmr *controller)
+{
+ unsigned_word attach_address;
+ int attach_space;
+ unsigned attach_size;
+ reg_property_spec reg;
+
+ if (hw_find_property (me, "reg") == NULL)
+ hw_abort (me, "Missing \"reg\" property");
+
+ if (!hw_find_reg_array_property (me, "reg", 0, &reg))
+ hw_abort (me, "\"reg\" property must contain one addr/size entry");
+
+ hw_unit_address_to_attach_address (hw_parent (me),
+ &reg.address,
+ &attach_space,
+ &attach_address,
+ me);
+ hw_unit_size_to_attach_size (hw_parent (me),
+ &reg.size,
+ &attach_size, me);
+
+ hw_attach_address (hw_parent (me), 0,
+ attach_space, attach_address, attach_size,
+ me);
+
+ if(hw_find_property(me, "clock") != NULL)
+ controller->clock_ticks = (unsigned_4) hw_find_integer_property(me, "clock");
+
+ if(hw_find_property(me, "ext") != NULL)
+ controller->ext_ticks = (unsigned_4) hw_find_integer_property(me, "ext");
+
+ controller->base_address = attach_address;
+}
+
+
+static void
+tx3904tmr_finish (struct hw *me)
+{
+ struct tx3904tmr *controller;
+
+ controller = HW_ZALLOC (me, struct tx3904tmr);
+ set_hw_data (me, controller);
+ set_hw_io_read_buffer (me, tx3904tmr_io_read_buffer);
+ set_hw_io_write_buffer (me, tx3904tmr_io_write_buffer);
+ set_hw_ports (me, tx3904tmr_ports);
+ set_hw_port_event (me, tx3904tmr_port_event);
+
+ /* Preset clock dividers */
+ controller->clock_ticks = 1;
+ controller->ext_ticks = 100;
+
+ /* Attach ourself to our parent bus */
+ attach_tx3904tmr_regs (me, controller);
+
+ /* Initialize to reset state */
+ controller->tcr =
+ controller->itmr =
+ controller->ccdr =
+ controller->pmgr =
+ controller->wtmr =
+ controller->tisr =
+ controller->trr = 0;
+ controller->cpra = controller->cprb = 0x00FFFFFF;
+ controller->ff = 0;
+}
+
+
+
+/* An event arrives on an interrupt port */
+
+static void
+tx3904tmr_port_event (struct hw *me,
+ int my_port,
+ struct hw *source,
+ int source_port,
+ int level)
+{
+ struct tx3904tmr *controller = hw_data (me);
+
+ switch (my_port)
+ {
+ case RESET_PORT:
+ {
+ HW_TRACE ((me, "reset"));
+
+ /* preset flip-flop to FFI value */
+ controller->ff = GET_PMGR_FFI(controller);
+
+ controller->tcr =
+ controller->itmr =
+ controller->ccdr =
+ controller->pmgr =
+ controller->wtmr =
+ controller->tisr =
+ controller->trr = 0;
+ controller->cpra = controller->cprb = 0x00FFFFFF;
+ break;
+ }
+
+ default:
+ hw_abort (me, "Event on unknown port %d", my_port);
+ break;
+ }
+}
+
+
+/* generic read/write */
+
+static unsigned
+tx3904tmr_io_read_buffer (struct hw *me,
+ void *dest,
+ int space,
+ unsigned_word base,
+ unsigned nr_bytes)
+{
+ struct tx3904tmr *controller = hw_data (me);
+ unsigned byte;
+
+ HW_TRACE ((me, "read 0x%08lx %d", (long) base, (int) nr_bytes));
+ for (byte = 0; byte < nr_bytes; byte++)
+ {
+ address_word address = base + byte;
+ int reg_number = (address - controller->base_address) / 4;
+ int reg_offset = (address - controller->base_address) % 4;
+ unsigned_4 register_value; /* in target byte order */
+
+ /* fill in entire register_value word */
+ switch (reg_number)
+ {
+ case TCR_REG: register_value = controller->tcr; break;
+ case TISR_REG: register_value = controller->tisr; break;
+ case CPRA_REG: register_value = controller->cpra; break;
+ case CPRB_REG: register_value = controller->cprb; break;
+ case ITMR_REG: register_value = controller->itmr; break;
+ case CCDR_REG: register_value = controller->ccdr; break;
+ case PMGR_REG: register_value = controller->pmgr; break;
+ case WTMR_REG: register_value = controller->wtmr; break;
+ case TRR_REG: register_value = controller->trr; break;
+ default: register_value = 0;
+ }
+
+ /* write requested byte out */
+ memcpy ((char*) dest + byte, ((char*)& register_value)+reg_offset, 1);
+ }
+
+ return nr_bytes;
+}
+
+
+
+static unsigned
+tx3904tmr_io_write_buffer (struct hw *me,
+ const void *source,
+ int space,
+ unsigned_word base,
+ unsigned nr_bytes)
+{
+ struct tx3904tmr *controller = hw_data (me);
+ unsigned byte;
+
+ HW_TRACE ((me, "write 0x%08lx %d", (long) base, (int) nr_bytes));
+ for (byte = 0; byte < nr_bytes; byte++)
+ {
+ address_word address = base + byte;
+ unsigned_1 write_byte = ((char*) source)[byte];
+ int reg_number = (address - controller->base_address) / 4;
+ int reg_offset = (address - controller->base_address) % 4;
+ unsigned_4* register_ptr;
+ unsigned_4 register_value;
+
+ /* fill in entire register_value word */
+ switch (reg_number)
+ {
+ case TCR_REG:
+ if(reg_offset == 0) /* first byte */
+ {
+ /* update register, but mask out NOP bits */
+ controller->tcr = (unsigned_4) (write_byte & 0xef);
+
+ /* Reset counter value if timer suspended and CRE is set. */
+ if(GET_TCR_TCE(controller) == 0 &&
+ GET_TCR_CRE(controller) == 1)
+ controller->trr = 0;
+
+ }
+ HW_TRACE ((me, "tcr: %08lx", (long) controller->tcr));
+ break;
+
+ case ITMR_REG:
+ if(reg_offset == 1) /* second byte */
+ {
+ SET_ITMR_TIIE(controller, write_byte & 0x80);
+ }
+ else if(reg_offset == 0) /* first byte */
+ {
+ SET_ITMR_TZCE(controller, write_byte & 0x01);
+ }
+ HW_TRACE ((me, "itmr: %08lx", (long) controller->itmr));
+ break;
+
+ case CCDR_REG:
+ if(reg_offset == 0) /* first byte */
+ {
+ controller->ccdr = write_byte & 0x07;
+ }
+ HW_TRACE ((me, "ccdr: %08lx", (long) controller->ccdr));
+ break;
+
+ case PMGR_REG:
+ if(reg_offset == 1) /* second byte */
+ {
+ SET_PMGR_TPIBE(controller, write_byte & 0x80);
+ SET_PMGR_TPIAE(controller, write_byte & 0x40);
+ }
+ else if(reg_offset == 0) /* first byte */
+ {
+ SET_PMGR_FFI(controller, write_byte & 0x01);
+ }
+ HW_TRACE ((me, "pmgr: %08lx", (long) controller->pmgr));
+ break;
+
+ case WTMR_REG:
+ if(reg_offset == 1) /* second byte */
+ {
+ SET_WTMR_TWIE(controller, write_byte & 0x80);
+ }
+ else if(reg_offset == 0) /* first byte */
+ {
+ SET_WTMR_WDIS(controller, write_byte & 0x80);
+ SET_WTMR_TWC(controller, write_byte & 0x01);
+ }
+ HW_TRACE ((me, "wtmr: %08lx", (long) controller->wtmr));
+ break;
+
+ case TISR_REG:
+ if(reg_offset == 0) /* first byte */
+ {
+ /* All bits must be zero in given byte, according to
+ spec. */
+
+ /* Send an "interrupt off" event on the interrupt port */
+ if(controller->tisr != 0) /* any interrupts active? */
+ {
+ hw_port_event(me, INT_PORT, 0);
+ }
+
+ /* clear interrupt status register */
+ controller->tisr = 0;
+ }
+ HW_TRACE ((me, "tisr: %08lx", (long) controller->tisr));
+ break;
+
+ case CPRA_REG:
+ if(reg_offset < 3) /* first, second, or third byte */
+ {
+ MBLIT32(controller->cpra, (reg_offset*8), (reg_offset*8+7), write_byte);
+ }
+ HW_TRACE ((me, "cpra: %08lx", (long) controller->cpra));
+ break;
+
+ case CPRB_REG:
+ if(reg_offset < 3) /* first, second, or third byte */
+ {
+ MBLIT32(controller->cprb, (reg_offset*8), (reg_offset*8+7), write_byte);
+ }
+ HW_TRACE ((me, "cprb: %08lx", (long) controller->cprb));
+ break;
+
+ default:
+ HW_TRACE ((me, "write to illegal register %d", reg_number));
+ }
+ } /* loop over bytes */
+
+ /* Schedule a timer event in near future, so we can increment or
+ stop the counter, to respond to register updates. */
+ hw_event_queue_schedule(me, 1, deliver_tx3904tmr_tick, NULL);
+
+ return nr_bytes;
+}
+
+
+
+/* Deliver a clock tick to the counter. */
+static void
+deliver_tx3904tmr_tick (struct hw *me,
+ void *data)
+{
+ struct tx3904tmr *controller = hw_data (me);
+ SIM_DESC sd = hw_system (me);
+ signed_8 this_ticks = sim_events_time(sd);
+
+ /* compute simulation ticks between last tick and this tick */
+ signed_8 warp = this_ticks - controller->last_ticks + controller->roundoff_ticks;
+ signed_8 divisor;
+ signed_8 quotient, reminder;
+
+ /* Check whether the timer ticking is enabled at this moment. This
+ largely a function of the TCE bit, but is also slightly
+ mode-dependent. */
+ switch(GET_TCR_TMODE(controller))
+ {
+ case 0: /* interval */
+ /* do not advance counter if TCE = 0 or if holding at count = CPRA */
+ if(GET_TCR_TCE(controller) == 0 ||
+ controller->trr == controller->cpra)
+ return;
+ break;
+
+ case 1: /* pulse generator */
+ /* do not advance counter if TCE = 0 */
+ if(GET_TCR_TCE(controller) == 0)
+ return;
+ break;
+
+ case 2: /* watchdog */
+ /* do not advance counter if TCE = 0 and WDIS = 1 */
+ if(GET_TCR_TCE(controller) == 0 &&
+ GET_WTMR_WDIS(controller) == 1)
+ return;
+ break;
+
+ case 3: /* disabled */
+ /* regardless of TCE, do not advance counter */
+ return;
+ }
+
+ /* In any of the above cases that return, a subsequent register
+ write will be needed to restart the timer. A tick event is
+ scheduled by any register write, so it is more efficient not to
+ reschedule dummy events here. */
+
+
+ /* find appropriate divisor etc. */
+ if(GET_TCR_CCS(controller) == 0) /* internal system clock */
+ {
+ /* apply internal clock divider */
+ if(GET_TCR_CCDE(controller)) /* divisor circuit enabled? */
+ divisor = controller->ext_ticks * (1 << (1 + GET_CCDR_CDR(controller)));
+ else
+ divisor = controller->ext_ticks;
+ }
+ else
+ {
+ divisor = controller->clock_ticks;
+ }
+
+ /* how many times to increase counter? */
+ quotient = warp / divisor;
+ reminder = warp % divisor;
+
+ /* NOTE: If the event rescheduling code works properly, the quotient
+ should never be larger than 1. That is, we should receive events
+ here at least as frequently as the simulated counter is supposed
+ to decrement. So the reminder (-> roundoff_ticks) will slowly
+ accumulate, with the quotient == 0. Once in a while, quotient
+ will equal 1. */
+
+ controller->roundoff_ticks = reminder;
+ controller->last_ticks = this_ticks;
+ while(quotient > 0) /* Is it time to increment counter? */
+ {
+ /* next 24-bit counter value */
+ unsigned_4 next_trr = (controller->trr + 1) % (1 << 24);
+ quotient --;
+
+ switch(GET_TCR_TMODE(controller))
+ {
+ case 0: /* interval timer mode */
+ {
+ /* Current or next counter value matches CPRA value? The
+ first case covers counter holding at maximum before
+ reset. The second case covers normal counting
+ behavior. */
+ if(controller->trr == controller->cpra ||
+ next_trr == controller->cpra)
+ {
+ /* likely hold CPRA value */
+ if(controller->trr == controller->cpra)
+ next_trr = controller->cpra;
+
+ SET_TISR_TIIS(controller);
+
+ /* Signal an interrupt if it is enabled with TIIE,
+ and if we just arrived at CPRA. Don't repeatedly
+ interrupt if holding due to TZCE=0 */
+ if(GET_ITMR_TIIE(controller) &&
+ next_trr != controller->trr)
+ {
+ hw_port_event(me, INT_PORT, 1);
+ }
+
+ /* Reset counter? */
+ if(GET_ITMR_TZCE(controller))
+ {
+ next_trr = 0;
+ }
+ }
+ }
+ break;
+
+ case 1: /* pulse generator mode */
+ {
+ /* first trip point */
+ if(next_trr == controller->cpra)
+ {
+ /* flip flip-flop & report */
+ controller->ff ^= 1;
+ hw_port_event(me, FF_PORT, controller->ff);
+ SET_TISR_TPIAS(controller);
+
+ /* signal interrupt */
+ if(GET_PMGR_TPIAE(controller))
+ {
+ hw_port_event(me, INT_PORT, 1);
+ }
+
+ }
+ /* second trip point */
+ else if(next_trr == controller->cprb)
+ {
+ /* flip flip-flop & report */
+ controller->ff ^= 1;
+ hw_port_event(me, FF_PORT, controller->ff);
+ SET_TISR_TPIBS(controller);
+
+ /* signal interrupt */
+ if(GET_PMGR_TPIBE(controller))
+ {
+ hw_port_event(me, INT_PORT, 1);
+ }
+
+ /* clear counter */
+ next_trr = 0;
+ }
+ }
+ break;
+
+ case 2: /* watchdog timer mode */
+ {
+ /* watchdog timer expiry */
+ if(next_trr == controller->cpra)
+ {
+ SET_TISR_TWIS(controller);
+
+ /* signal interrupt */
+ if(GET_WTMR_TWIE(controller))
+ {
+ hw_port_event(me, INT_PORT, 1);
+ }
+
+ /* clear counter */
+ next_trr = 0;
+ }
+ }
+ break;
+
+ case 3: /* disabled */
+ default:
+ }
+
+ /* update counter and report */
+ controller->trr = next_trr;
+ HW_TRACE ((me, "counter trr %d tisr %x", controller->trr, controller->tisr));
+ } /* end quotient loop */
+
+ /* Reschedule a timer event in near future, so we can increment the
+ counter again. Set the event about 50% of divisor time away, so
+ we will experience roughly two events per counter increment. */
+ hw_event_queue_schedule(me, divisor/2, deliver_tx3904tmr_tick, NULL);
+}
+
+
+
+
+const struct hw_descriptor dv_tx3904tmr_descriptor[] = {
+ { "tx3904tmr", tx3904tmr_finish, },
+ { NULL },
+};
diff --git a/sim/mips/interp.c b/sim/mips/interp.c
index 4738f7f..e0ec3e3 100644
--- a/sim/mips/interp.c
+++ b/sim/mips/interp.c
@@ -518,11 +518,17 @@ sim_open (kind, cb, abfd, argv)
0xA8000000);
/* --- simulated devices --- */
- sim_hw_parse (sd, "/tx3904irc@0xffffc00/reg 0xffffc000 0x20");
+ sim_hw_parse (sd, "/tx3904irc@0xffffc000/reg 0xffffc000 0x20");
sim_hw_parse (sd, "/tx3904cpu");
+ sim_hw_parse (sd, "/tx3904tmr@0xfffff000/reg 0xfffff000 0x100");
+ sim_hw_parse (sd, "/tx3904tmr@0xfffff100/reg 0xfffff100 0x100");
+ sim_hw_parse (sd, "/tx3904tmr@0xfffff200/reg 0xfffff200 0x100");
/* -- device connections --- */
sim_hw_parse (sd, "/tx3904irc > ip level /tx3904cpu");
+ sim_hw_parse (sd, "/tx3904tmr@0xfffff000 > int tmr0 /tx3904irc");
+ sim_hw_parse (sd, "/tx3904tmr@0xfffff100 > int tmr1 /tx3904irc");
+ sim_hw_parse (sd, "/tx3904tmr@0xfffff200 > int tmr2 /tx3904irc");
if(! strcmp(board, BOARD_JMR3904_DEBUG))
{