* 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.
This commit is contained in:
parent
0e797366ef
commit
da040f2a6c
8 changed files with 734 additions and 28 deletions
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
2
sim/mips/configure
vendored
2
sim/mips/configure
vendored
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 },
|
||||
};
|
||||
|
|
|
@ -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 },
|
||||
};
|
||||
|
|
681
sim/mips/dv-tx3904tmr.c
Normal file
681
sim/mips/dv-tx3904tmr.c
Normal file
|
@ -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, ®))
|
||||
hw_abort (me, "\"reg\" property must contain one addr/size entry");
|
||||
|
||||
hw_unit_address_to_attach_address (hw_parent (me),
|
||||
®.address,
|
||||
&attach_space,
|
||||
&attach_address,
|
||||
me);
|
||||
hw_unit_size_to_attach_size (hw_parent (me),
|
||||
®.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 },
|
||||
};
|
|
@ -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))
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue