From: Andrew Cagney Date: Wed, 25 Mar 1998 04:15:38 +0000 (+0000) Subject: * interp.c (sim_open): Create second 1mb memory region at 0x40000000. X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=6100784a60a46ce81f1264a630cf003f74df78ce;p=binutils-gdb.git * interp.c (sim_open): Create second 1mb memory region at 0x40000000. (sim_open): Create a device tree. (sim-hw.h): Include. (do_interrupt): Delete, needs to use dv-mn103cpu.c * dv-mn103int.c, dv-mn103cpu.c: New files. --- diff --git a/sim/mn10300/.Sanitize b/sim/mn10300/.Sanitize index 6918ca8661e..d49e8fbaa8c 100644 --- a/sim/mn10300/.Sanitize +++ b/sim/mn10300/.Sanitize @@ -28,6 +28,8 @@ Makefile.in config.in configure configure.in +dv-mn103cpu.c +dv-mn103int.c mn10300_sim.h gencode.c interp.c diff --git a/sim/mn10300/ChangeLog b/sim/mn10300/ChangeLog index 02a8b9e9679..7a3f3f5337f 100644 --- a/sim/mn10300/ChangeLog +++ b/sim/mn10300/ChangeLog @@ -1,3 +1,13 @@ +Wed Mar 25 15:08:49 1998 Andrew Cagney + + * interp.c (sim_open): Create second 1mb memory region at + 0x40000000. + (sim_open): Create a device tree. + (sim-hw.h): Include. + (do_interrupt): Delete, needs to use dv-mn103cpu.c + + * dv-mn103int.c, dv-mn103cpu.c: New files. + Wed Mar 25 08:47:38 1998 Andrew Cagney * mn10300_sim.h (EXTRACT_PSW_LM, INSERT_PSW_LM, PSW_IE, PSW_LM): diff --git a/sim/mn10300/configure b/sim/mn10300/configure index b3eefecc226..c0d6273737f 100755 --- a/sim/mn10300/configure +++ b/sim/mn10300/configure @@ -114,6 +114,8 @@ ac_help="$ac_help --enable-sim-reserved-bits Specify whether to check reserved bits in instruction." ac_help="$ac_help --enable-sim-bitsize=N Specify target bitsize (32 or 64)." +ac_help="$ac_help + --enable-sim-hardware=LIST Specify the hardware to be included in the build." ac_help="$ac_help --enable-sim-common Enable common simulator" @@ -626,7 +628,7 @@ fi echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6 -echo "configure:630: checking how to run the C preprocessor" >&5 +echo "configure:632: checking how to run the C preprocessor" >&5 # On Suns, sometimes $CPP names a directory. if test -n "$CPP" && test -d "$CPP"; then CPP= @@ -641,13 +643,13 @@ else # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. cat > conftest.$ac_ext < Syntax Error EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:651: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:653: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out` if test -z "$ac_err"; then : @@ -658,13 +660,13 @@ else rm -rf conftest* CPP="${CC-cc} -E -traditional-cpp" cat > conftest.$ac_ext < Syntax Error EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:668: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:670: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out` if test -z "$ac_err"; then : @@ -737,7 +739,7 @@ else { echo "configure: error: can not run $ac_config_sub" 1>&2; exit 1; } fi echo $ac_n "checking host system type""... $ac_c" 1>&6 -echo "configure:741: checking host system type" >&5 +echo "configure:743: checking host system type" >&5 host_alias=$host case "$host_alias" in @@ -758,7 +760,7 @@ host_os=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` echo "$ac_t""$host" 1>&6 echo $ac_n "checking target system type""... $ac_c" 1>&6 -echo "configure:762: checking target system type" >&5 +echo "configure:764: checking target system type" >&5 target_alias=$target case "$target_alias" in @@ -776,7 +778,7 @@ target_os=`echo $target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` echo "$ac_t""$target" 1>&6 echo $ac_n "checking build system type""... $ac_c" 1>&6 -echo "configure:780: checking build system type" >&5 +echo "configure:782: checking build system type" >&5 build_alias=$build case "$build_alias" in @@ -820,7 +822,7 @@ test "$program_transform_name" = "" && program_transform_name="s,x,x," # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:824: checking for $ac_word" >&5 +echo "configure:826: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -849,7 +851,7 @@ if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:853: checking for $ac_word" >&5 +echo "configure:855: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -897,7 +899,7 @@ fi fi echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6 -echo "configure:901: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5 +echo "configure:903: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5 ac_ext=c # CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. @@ -907,11 +909,11 @@ ac_link='${CC-cc} -o conftest $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS cross_compiling=$ac_cv_prog_cc_cross cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then +if { (eval echo configure:917: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then ac_cv_prog_cc_works=yes # If we can't run a trivial program, we are probably using a cross compiler. if (./conftest; exit) 2>/dev/null; then @@ -931,12 +933,12 @@ if test $ac_cv_prog_cc_works = no; then { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; } fi echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6 -echo "configure:935: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5 +echo "configure:937: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5 echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6 cross_compiling=$ac_cv_prog_cc_cross echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6 -echo "configure:940: checking whether we are using GNU C" >&5 +echo "configure:942: checking whether we are using GNU C" >&5 if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -945,7 +947,7 @@ else yes; #endif EOF -if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:949: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then +if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:951: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then ac_cv_prog_gcc=yes else ac_cv_prog_gcc=no @@ -960,7 +962,7 @@ if test $ac_cv_prog_gcc = yes; then ac_save_CFLAGS="$CFLAGS" CFLAGS= echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6 -echo "configure:964: checking whether ${CC-cc} accepts -g" >&5 +echo "configure:966: checking whether ${CC-cc} accepts -g" >&5 if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -999,7 +1001,7 @@ fi # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" # ./install, which can be erroneously created by make from ./install.sh. echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6 -echo "configure:1003: checking for a BSD compatible install" >&5 +echo "configure:1005: checking for a BSD compatible install" >&5 if test -z "$INSTALL"; then if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -1065,7 +1067,7 @@ AR=${AR-ar} # Extract the first word of "ranlib", so it can be a program name with args. set dummy ranlib; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:1069: checking for $ac_word" >&5 +echo "configure:1071: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_RANLIB'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1099,17 +1101,17 @@ for ac_hdr in stdlib.h string.h strings.h unistd.h time.h do ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 -echo "configure:1103: checking for $ac_hdr" >&5 +echo "configure:1105: checking for $ac_hdr" >&5 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:1113: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:1115: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out` if test -z "$ac_err"; then rm -rf conftest* @@ -1139,17 +1141,17 @@ for ac_hdr in sys/time.h sys/resource.h do ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 -echo "configure:1143: checking for $ac_hdr" >&5 +echo "configure:1145: checking for $ac_hdr" >&5 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:1153: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:1155: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out` if test -z "$ac_err"; then rm -rf conftest* @@ -1179,17 +1181,17 @@ for ac_hdr in fcntl.h do ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 -echo "configure:1183: checking for $ac_hdr" >&5 +echo "configure:1185: checking for $ac_hdr" >&5 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:1193: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:1195: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out` if test -z "$ac_err"; then rm -rf conftest* @@ -1218,12 +1220,12 @@ done for ac_func in getrusage time sigaction do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 -echo "configure:1222: checking for $ac_func" >&5 +echo "configure:1224: checking for $ac_func" >&5 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then +if { (eval echo configure:1252: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else @@ -1419,12 +1421,12 @@ fi echo $ac_n "checking return type of signal handlers""... $ac_c" 1>&6 -echo "configure:1423: checking return type of signal handlers" >&5 +echo "configure:1425: checking return type of signal handlers" >&5 if eval "test \"`echo '$''{'ac_cv_type_signal'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #include @@ -1441,7 +1443,7 @@ int main() { int i; ; return 0; } EOF -if { (eval echo configure:1445: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:1447: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* ac_cv_type_signal=void else @@ -1591,14 +1593,14 @@ else if test "x$cross_compiling" = "xno"; then echo $ac_n "checking whether byte ordering is bigendian""... $ac_c" 1>&6 -echo "configure:1595: checking whether byte ordering is bigendian" >&5 +echo "configure:1597: checking whether byte ordering is bigendian" >&5 if eval "test \"`echo '$''{'ac_cv_c_bigendian'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else ac_cv_c_bigendian=unknown # See if sys/param.h defines the BYTE_ORDER macro. cat > conftest.$ac_ext < #include @@ -1609,11 +1611,11 @@ int main() { #endif ; return 0; } EOF -if { (eval echo configure:1613: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:1615: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* # It does; now see whether it defined to BIG_ENDIAN or not. cat > conftest.$ac_ext < #include @@ -1624,7 +1626,7 @@ int main() { #endif ; return 0; } EOF -if { (eval echo configure:1628: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then +if { (eval echo configure:1630: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* ac_cv_c_bigendian=yes else @@ -1644,7 +1646,7 @@ if test "$cross_compiling" = yes; then { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; } else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null +if { (eval echo configure:1663: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null then ac_cv_c_bigendian=no else @@ -1784,15 +1786,51 @@ fi fi +sim_hardware="-DWITH_HW=1" +sim_hw_obj="hw-device.o hw-ports.o hw-properties.o hw-base.o hw-tree.o" +hardware="core pal glue mn103cpu mn103int" +# Check whether --enable-sim-hardware or --disable-sim-hardware was given. +if test "${enable_sim_hardware+set}" = set; then + enableval="$enable_sim_hardware" + +case "${enableval}" in + yes) ;; + no) hardware=""; sim_hardware="-DWITH_HW=0"; sim_hw_obj="";; + ,*) hardware="${hardware} `echo ${enableval} | sed -e 's/,/ /'`";; + *,) hardware="`echo ${enableval} | sed -e 's/,/ /'` ${hardware}";; + *) hardware="`echo ${enableval} | sed -e 's/,/ /'`"'';; +esac +sim_hw="" +for i in x $hardware ; do + case " $f " in + x) ;; + *" $i "*) ;; + *) sim_hw="$sim_hw $i" ;; + esac +done +sim_hw_obj="$sim_hw_obj `echo $sim_hw | sed -e 's/\([^ ]*\)/dv-\1.o/g'`" +if test x"$silent" != x"yes" && test x"$hardware" != x""; then + echo "Setting hardware to $sim_hardware, $sim_hw, $sim_hw_obj" +fi +else + +sim_hw="$hardware" +sim_hw_obj="$sim_hw_obj `echo $sim_hw | sed -e 's/\([^ ]*\)/dv-\1.o/g'`" +if test x"$silent" != x"yes"; then + echo "Setting hardware to $sim_hardware, $sim_hw, $sim_hw_obj" +fi +fi + + for ac_func in time chmod utime fork execve execv chown do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 -echo "configure:1791: checking for $ac_func" >&5 +echo "configure:1829: checking for $ac_func" >&5 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then +if { (eval echo configure:1857: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else @@ -1843,17 +1881,17 @@ for ac_hdr in unistd.h stdlib.h string.h strings.h utime.h time.h do ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 -echo "configure:1847: checking for $ac_hdr" >&5 +echo "configure:1885: checking for $ac_hdr" >&5 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:1857: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +{ (eval echo configure:1895: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out` if test -z "$ac_err"; then rm -rf conftest* diff --git a/sim/mn10300/configure.in b/sim/mn10300/configure.in index 7bbd463b635..8aeee593cf8 100644 --- a/sim/mn10300/configure.in +++ b/sim/mn10300/configure.in @@ -12,6 +12,7 @@ SIM_AC_OPTION_HOSTENDIAN SIM_AC_OPTION_WARNINGS SIM_AC_OPTION_RESERVED_BITS SIM_AC_OPTION_BITSIZE(32,31) +SIM_AC_OPTION_HARDWARE(,mn103cpu mn103int) AC_CHECK_FUNCS(time chmod utime fork execve execv chown) AC_CHECK_HEADERS(unistd.h stdlib.h string.h strings.h utime.h time.h) diff --git a/sim/mn10300/dv-mn103cpu.c b/sim/mn10300/dv-mn103cpu.c new file mode 100644 index 00000000000..87c684e3a16 --- /dev/null +++ b/sim/mn10300/dv-mn103cpu.c @@ -0,0 +1,431 @@ +/* This file is part of the program GDB, the GU 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-base.h" + +/* DEVICE + + + mn103cpu - mn10300 cpu virtual device + + + DESCRIPTION + + + Implements the external mn10300 functionality. This includes the + delivery of of interrupts generated from other devices and the + handling of device specific registers. + + + PROPERTIES + + + reg =
+ + Specify the address of the mn10300's control register block. This + block contains the Interrupt Vector Registers. + + The reg property value `0x20000000 0x42' locates the register block + at the address specified in the mn10300 user guide. + + + PORTS + + + reset (input) + + Currently ignored. + + + nmi (input) + + Deliver a non-maskable interrupt to the processor. + + + level (input) + + Maskable interrupt level port port. The interrupt controller + notifies the processor of any change in the level of pending + requested interrupts via this port. + + + ack (output) + + Output signal indicating that the processor is delivering a level + interrupt. The value passed with the event specfies the level of + the interrupt being delivered. + + + BUGS + + + When delivering an interrupt, this code assumes that there is only + one processor (number 0). + + This code does not attempt to be efficient at handling pending + interrupts. It simply schedules the interrupt delivery handler + every instruction cycle until all pending interrupts go away. An + alternative implementation might modify instructions that change + the PSW and have them check to see if the change makes an interrupt + delivery possible. + + */ + + +/* The interrupt vectors */ + +enum { NR_VECTORS = 7, }; + + +/* The interrupt controller register address blocks */ + +struct mn103cpu_block { + unsigned_word base; + unsigned_word bound; +}; + + +struct mn103cpu { + struct mn103cpu_block block; + hw_event *pending_handler; + int pending_level; + int pending_nmi; + int pending_reset; + /* the visible registers */ + unsigned16 interrupt_vector[NR_VECTORS]; + unsigned16 internal_memory_control; + unsigned16 cpu_mode; +}; + + + +/* input port ID's */ + +enum { + RESET_PORT, + NMI_PORT, + LEVEL_PORT, +}; + + +/* input port ID's */ + +enum { + ACK_PORT, +}; + +static const struct hw_port_descriptor mn103cpu_ports[] = { + + /* interrupt inputs */ + { "reset", RESET_PORT, 0, input_port, }, + { "nmi", NMI_PORT, 0, input_port, }, + { "level", LEVEL_PORT, 0, input_port, }, + + /* interrupt ack (latch) output from cpu */ + { "ack", ACK_PORT, 0, output_port, }, + + { NULL, }, +}; + + +/* Finish off the partially created hw device. Attach our local + callbacks. Wire up our port names etc */ + +static hw_io_read_buffer_callback mn103cpu_io_read_buffer; +static hw_io_write_buffer_callback mn103cpu_io_write_buffer; +static hw_port_event_callback mn103cpu_port_event; + +static void +attach_mn103cpu_regs (struct hw *me, + struct mn103cpu *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 three addr/size entries"); + hw_unit_address_to_attach_address (hw_parent (me), + ®.address, + &attach_space, + &attach_address, + me); + controller->block.base = attach_address; + hw_unit_size_to_attach_size (hw_parent (me), + ®.size, + &attach_size, me); + controller->block.bound = attach_address + (attach_size - 1); + if ((controller->block.base & 3) != 0) + hw_abort (me, "cpu register block must be 4 byte aligned"); + hw_attach_address (hw_parent (me), + 0, + attach_space, attach_address, attach_size, + me); +} + + +static void +mn103cpu_finish (struct hw *me) +{ + struct mn103cpu *controller; + + controller = HW_ZALLOC (me, struct mn103cpu); + set_hw_data (me, controller); + set_hw_io_read_buffer (me, mn103cpu_io_read_buffer); + set_hw_io_write_buffer (me, mn103cpu_io_write_buffer); + set_hw_ports (me, mn103cpu_ports); + set_hw_port_event (me, mn103cpu_port_event); + + /* Attach ourself to our parent bus */ + attach_mn103cpu_regs (me, controller); + + /* Initialize the read-only registers */ + controller->pending_level = 7; /* FIXME */ + /* ... */ +} + + + +/* An event arrives on an interrupt port */ + +static void +deliver_mn103cpu_interrupt (struct hw *me, + void *data) +{ + struct mn103cpu *controller = hw_data (me); + SIM_DESC simulator = hw_system (me); + sim_cpu *cpu = STATE_CPU (simulator, 0); + + if (controller->pending_reset) + { + controller->pending_reset = 0; + /* need to clear all registers et.al! */ + HW_TRACE ((me, "Reset!")); + hw_abort (me, "Reset!"); + } + else if (controller->pending_nmi) + { + controller->pending_nmi = 0; + store_half (SP - 4, CIA_GET (cpu)); + store_half (SP - 8, PSW); + PSW &= ~PSW_IE; + SP = SP - 8; + CIA_SET (cpu, 0x40000008); + HW_TRACE ((me, "nmi pc=0x%08lx psw=0x%04x sp=0x%08lx", + (long) CIA_GET (cpu), (unsigned) PSW, (long) SP)); + } + else if ((controller->pending_level < EXTRACT_PSW_LM) + && (PSW & PSW_IE)) + { + /* Don't clear pending level. Request continues to be pending + until the interrupt controller clears/changes it */ + store_half (SP - 4, CIA_GET (cpu)); + store_half (SP - 8, PSW); + PSW &= ~PSW_IE; + PSW &= ~PSW_LM; + PSW |= INSERT_PSW_LM (controller->pending_level); + SP = SP - 8; + CIA_SET (cpu, 0x40000000 + controller->interrupt_vector[controller->pending_level]); + HW_TRACE ((me, "port-out ack %d", controller->pending_level)); + hw_port_event (me, ACK_PORT, controller->pending_level, NULL, NULL_CIA); + HW_TRACE ((me, "int level=%d pc=0x%08lx psw=0x%04x sp=0x%08lx", + controller->pending_level, + (long) CIA_GET (cpu), (unsigned) PSW, (long) SP)); + } + + /* As long as there is the potential need to deliver an interrupt we + keep rescheduling this routine. */ + if (controller->pending_level < 7) /* FIXME */ + { + if (controller->pending_handler != NULL) + controller->pending_handler = + hw_event_queue_schedule (me, 1, deliver_mn103cpu_interrupt, NULL); + } + +} + + +static void +mn103cpu_port_event (struct hw *me, + int my_port, + struct hw *source, + int source_port, + int level, + sim_cpu *processor, + sim_cia cia) +{ + struct mn103cpu *controller = hw_data (me); + + /* Schedule our event handler *now* */ + if (controller->pending_handler == NULL) + controller->pending_handler = + hw_event_queue_schedule (me, 0, deliver_mn103cpu_interrupt, NULL); + + switch (my_port) + { + + case RESET_PORT: + controller->pending_reset = 1; + HW_TRACE ((me, "port-in reset")); + break; + + case NMI_PORT: + controller->pending_nmi = 1; + HW_TRACE ((me, "port-in nmi")); + break; + + case LEVEL_PORT: + controller->pending_level = level; + HW_TRACE ((me, "port-in level=%d", level)); + break; + + default: + hw_abort (me, "bad switch"); + break; + + } +} + + +/* Read/write to a CPU register */ + +enum mn103cpu_regs { + INVALID_REG, + IVR0_REG, + IVR1_REG, + IVR2_REG, + IVR3_REG, + IVR4_REG, + IVR5_REG, + IVR6_REG, + IMCR_REG, + CPUM_REG, +}; + +static enum mn103cpu_regs +decode_mn103cpu_addr (struct hw *me, + struct mn103cpu *controller, + unsigned_word base) +{ + switch (base - controller->block.base) + { + case 0x000: return IVR0_REG; + case 0x004: return IVR1_REG; + case 0x008: return IVR2_REG; + case 0x00c: return IVR3_REG; + case 0x010: return IVR4_REG; + case 0x014: return IVR5_REG; + case 0x018: return IVR6_REG; + case 0x020: return IMCR_REG; + case 0x040: return CPUM_REG; + default: return INVALID_REG; + } +} + +static unsigned +mn103cpu_io_read_buffer (struct hw *me, + void *dest, + int space, + unsigned_word base, + unsigned nr_bytes, + sim_cpu *processor, + sim_cia cia) +{ + struct mn103cpu *controller = hw_data (me); + unsigned16 val = 0; + enum mn103cpu_regs reg = decode_mn103cpu_addr (me, controller, base); + + switch (reg) + { + case IVR0_REG: + case IVR1_REG: + case IVR2_REG: + case IVR3_REG: + case IVR4_REG: + case IVR5_REG: + case IVR6_REG: + val = controller->interrupt_vector[reg - IVR0_REG]; + break; + case IMCR_REG: + val = controller->internal_memory_control; + break; + case CPUM_REG: + val = controller->cpu_mode; + break; + default: + /* just ignore the read */ + break; + } + + if (nr_bytes == 2) + *(unsigned16*) dest = H2LE_2 (val); + + return nr_bytes; +} + +static unsigned +mn103cpu_io_write_buffer (struct hw *me, + const void *source, + int space, + unsigned_word base, + unsigned nr_bytes, + sim_cpu *cpu, + sim_cia cia) +{ + struct mn103cpu *controller = hw_data (me); + unsigned16 val; + enum mn103cpu_regs reg; + + if (nr_bytes != 2) + hw_abort (me, "must be two byte write"); + + reg = decode_mn103cpu_addr (me, controller, base); + val = LE2H_2 (* (unsigned16 *) source); + + switch (reg) + { + case IVR0_REG: + case IVR1_REG: + case IVR2_REG: + case IVR3_REG: + case IVR4_REG: + case IVR5_REG: + case IVR6_REG: + controller->interrupt_vector[reg - IVR0_REG] = val; + HW_TRACE ((me, "ivr%d = 0x%04lx", reg - IVR0_REG, (long) val)); + break; + default: + /* just ignore the write */ + break; + } + + return nr_bytes; +} + + +const struct hw_device_descriptor dv_mn103cpu_descriptor[] = { + { "mn103cpu", mn103cpu_finish, }, + { NULL }, +}; diff --git a/sim/mn10300/dv-mn103int.c b/sim/mn10300/dv-mn103int.c new file mode 100644 index 00000000000..061b9b871ab --- /dev/null +++ b/sim/mn10300/dv-mn103int.c @@ -0,0 +1,785 @@ +/* This file is part of the program GDB, the GU 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-base.h" + +/* DEVICE + + + mn103int - mn10300 interrupt controller + + + DESCRIPTION + + + Implements the mn10300 interrupt controller described in the + mn10300 user guide. + + + PROPERTIES + + + reg = + + Specify the address of the ICR (total of 25 registers), IAGR and + EXTMD registers (within the parent bus). + + The reg property value `0x34000100 0x68 0x34000200 0x8 0x3400280 + 0x8' locates the interrupt controller at the addresses specified in + the mn10300 interrupt controller user guide. + + + PORTS + + + nmi (output) + + Non-maskable interrupt output port. An event on this output ports + indicates a NMI request from the interrupt controller. The value + attached to the event should be ignored. + + + level (output) + + Maskable interrupt level output port. An event on this output port + indicates a maskable interrupt request at the specified level. The + event value defines the level being requested. + + The interrupt controller will generate an event on this port + whenever there is a change to the internal state of the interrupt + controller. + + + ack (input) + + Signal from processor indicating that a maskable interrupt has been + accepted and the interrupt controller should latch the IAGR with + value of the current highest priority interrupting group. + + The event value is the interrupt level being accepted by the + processor. It should be consistent with the most recent LEVEL sent + to the processor from the interrupt controller. + + + int[0..100] (input) + + Level or edge triggered interrupt input port. Each of the 25 + groups (0..24) can have up to 4 (0..3) interrupt inputs. The + interpretation of a port event/value is determined by the + configuration of the corresponding interrupt group. + + For convenience, numerous aliases to these interrupt inputs are + provided. + + + BUGS + + + For edge triggered interrupts, the interrupt controller does not + differentiate between POSITIVE (rising) and NEGATIVE (falling) + edges. Instead any input port event is considered to be an + interrupt trigger. + + For level sensative interrupts, the interrupt controller ignores + active HIGH/LOW settings and instead always interprets a nonzero + port value as an interupt assertion and a zero port value as a + negation. + + */ + + +/* The interrupt groups - numbered according to mn10300 convention */ + +enum mn103int_trigger { + ACTIVE_LOW, + ACTIVE_HIGH, + POSITIVE_EDGE, + NEGATIVE_EDGE, +}; + +enum mn103int_type { + NMI_GROUP, + INT_GROUP, +}; + +struct mn103int_group { + int level; + unsigned enable; + unsigned request; + unsigned input; + enum mn103int_trigger trigger; + enum mn103int_type type; +}; + +enum { + FIRST_NMI_GROUP = 0, + LAST_NMI_GROUP = 1, + FIRST_INT_GROUP = 2, + LAST_INT_GROUP = 24, + NR_GROUPS, +}; + +enum { + LOWEST_LEVEL = 7, +}; + +/* The interrupt controller register address blocks */ + +struct mn103int_block { + unsigned_word base; + unsigned_word bound; +}; + +enum { ICR_BLOCK, IAGR_BLOCK, EXTMD_BLOCK, NR_BLOCKS }; + + +struct mn103int { + struct mn103int_block block[NR_BLOCKS]; + struct mn103int_group group[NR_GROUPS]; + unsigned interrupt_accepted_group; +}; + + + +/* output port ID's */ + +enum { + NMI_PORT, + LEVEL_PORT, +}; + + +/* input port ID's */ + +enum { + G0_PORT = 0, + G1_PORT = 4, + G2_PORT = 8, + G3_PORT = 12, + G4_PORT = 16, + G5_PORT = 20, + G6_PORT = 24, + G7_PORT = 28, + G8_PORT = 32, + G9_PORT = 36, + G10_PORT = 40, + G11_PORT = 44, + G12_PORT = 48, + G13_PORT = 52, + G14_PORT = 56, + G15_PORT = 60, + G16_PORT = 64, + G17_PORT = 68, + G18_PORT = 72, + G19_PORT = 76, + G20_PORT = 80, + G21_PORT = 84, + G22_PORT = 88, + G23_PORT = 92, + G24_PORT = 96, + NR_G_PORTS = 100, + ACK_PORT, +}; + +static const struct hw_port_descriptor mn103int_ports[] = { + + /* interrupt outputs */ + + { "nmi", NMI_PORT, 0, output_port, }, + { "level", LEVEL_PORT, 0, output_port, }, + + /* interrupt ack (latch) input from cpu */ + + { "ack", ACK_PORT, 0, input_port, }, + + /* interrupt inputs (as names) */ + + { "nmirq", G0_PORT + 0, 0, input_port, }, + { "watchdog", G0_PORT + 1, 0, input_port, }, + { "syserr", G0_PORT + 2, 0, input_port, }, + + { "timer-0-underflow", G2_PORT + 0, 0, input_port, }, + { "timer-1-underflow", G2_PORT + 1, 0, input_port, }, + { "timer-2-underflow", G2_PORT + 2, 0, input_port, }, + { "timer-3-underflow", G2_PORT + 3, 0, input_port, }, + { "timer-4-underflow", G3_PORT + 0, 0, input_port, }, + { "timer-5-underflow", G3_PORT + 1, 0, input_port, }, + { "timer-6-underflow", G3_PORT + 2, 0, input_port, }, + { "timer-7-underflow", G3_PORT + 3, 0, input_port, }, + + { "timer-8-underflow", G4_PORT + 0, 0, input_port, }, + { "timer-8-compare-a", G4_PORT + 1, 0, input_port, }, + { "timer-8-compare-b", G4_PORT + 2, 0, input_port, }, + + { "timer-9-underflow", G5_PORT + 0, 0, input_port, }, + { "timer-9-compare-a", G5_PORT + 1, 0, input_port, }, + { "timer-9-compare-b", G5_PORT + 2, 0, input_port, }, + + { "timer-10-underflow", G6_PORT + 0, 0, input_port, }, + { "timer-10-compare-a", G6_PORT + 1, 0, input_port, }, + { "timer-10-compare-b", G6_PORT + 2, 0, input_port, }, + { "timer-10-compare-c", G6_PORT + 3, 0, input_port, }, + + { "timer-11-underflow", G7_PORT + 0, 0, input_port, }, + { "timer-11-compare-a", G7_PORT + 1, 0, input_port, }, + { "timer-11-compare-b", G7_PORT + 2, 0, input_port, }, + { "timer-11-compare-c", G7_PORT + 3, 0, input_port, }, + + { "timer-12-underflow", G8_PORT + 0, 0, input_port, }, + { "timer-12-compare-a", G8_PORT + 1, 0, input_port, }, + { "timer-12-compare-b", G8_PORT + 2, 0, input_port, }, + { "timer-12-compare-c", G8_PORT + 3, 0, input_port, }, + + { "timer-11-compare-d", G9_PORT + 0, 0, input_port, }, + { "timer-12-compare-d", G9_PORT + 1, 0, input_port, }, + + { "dma-0-end", G10_PORT, 0, input_port, }, + { "dma-1-end", G11_PORT, 0, input_port, }, + { "dma-2-end", G12_PORT, 0, input_port, }, + { "dma-3-end", G13_PORT, 0, input_port, }, + + { "serial-0-recieve", G14_PORT + 0, 0, input_port, }, + { "serial-0-transmit", G14_PORT + 1, 0, input_port, }, + + { "serial-1-recieve", G15_PORT + 0, 0, input_port, }, + { "serial-1-transmit", G15_PORT + 1, 0, input_port, }, + + { "irq-0", G16_PORT, 0, input_port, }, + { "irq-1", G17_PORT, 0, input_port, }, + { "irq-2", G18_PORT, 0, input_port, }, + { "irq-3", G19_PORT, 0, input_port, }, + { "irq-4", G20_PORT, 0, input_port, }, + { "irq-5", G21_PORT, 0, input_port, }, + { "irq-6", G22_PORT, 0, input_port, }, + { "irq-7", G23_PORT, 0, input_port, }, + + { "ad-end", G24_PORT, 0, input_port, }, + + /* interrupt inputs (as generic numbers) */ + + { "int", 0, NR_G_PORTS, input_port, }, + + { NULL, }, +}; + + +/* Macros for extracting/restoring the various register bits */ + +#define EXTRACT_ID(X) (LSEXTRACTED8 ((X), 3, 0)) +#define INSERT_ID(X) (LSINSERTED8 ((X), 3, 0)) + +#define EXTRACT_IR(X) (LSEXTRACTED8 ((X), 7, 4)) +#define INSERT_IR(X) (LSINSERTED8 ((X), 7, 4)) + +#define EXTRACT_IE(X) (LSEXTRACTED8 ((X), 3, 0)) +#define INSERT_IE(X) (LSINSERTED8 ((X), 3, 0)) + +#define EXTRACT_LV(X) (LSEXTRACTED8 ((X), 6, 4)) +#define INSERT_LV(X) (LSINSERTED8 ((X), 6, 4)) + + + +/* Finish off the partially created hw device. Attach our local + callbacks. Wire up our port names etc */ + +static hw_io_read_buffer_callback mn103int_io_read_buffer; +static hw_io_write_buffer_callback mn103int_io_write_buffer; +static hw_port_event_callback mn103int_port_event; + +static void +attach_mn103int_regs (struct hw *me, + struct mn103int *controller) +{ + int i; + if (hw_find_property (me, "reg") == NULL) + hw_abort (me, "Missing \"reg\" property"); + for (i = 0; i < NR_BLOCKS; i++) + { + unsigned_word attach_address; + int attach_space; + unsigned attach_size; + reg_property_spec reg; + if (!hw_find_reg_array_property (me, "reg", i, ®)) + hw_abort (me, "\"reg\" property must contain three addr/size entries"); + hw_unit_address_to_attach_address (hw_parent (me), + ®.address, + &attach_space, + &attach_address, + me); + controller->block[i].base = attach_address; + hw_unit_size_to_attach_size (hw_parent (me), + ®.size, + &attach_size, me); + controller->block[i].bound = attach_address + (attach_size - 1); + hw_attach_address (hw_parent (me), + 0, + attach_space, attach_address, attach_size, + me); + } +} + +static void +mn103int_finish (struct hw *me) +{ + int gid; + struct mn103int *controller; + + controller = HW_ZALLOC (me, struct mn103int); + set_hw_data (me, controller); + set_hw_io_read_buffer (me, mn103int_io_read_buffer); + set_hw_io_write_buffer (me, mn103int_io_write_buffer); + set_hw_ports (me, mn103int_ports); + set_hw_port_event (me, mn103int_port_event); + + /* Attach ourself to our parent bus */ + attach_mn103int_regs (me, controller); + + /* Initialize all the groups according to their default configuration */ + for (gid = 0; gid < NR_GROUPS; gid++) + { + struct mn103int_group *group = &controller->group[gid]; + group->enable = 0xf; + group->trigger = NEGATIVE_EDGE; + if (FIRST_NMI_GROUP <= gid && gid <= LAST_NMI_GROUP) + { + group->type = NMI_GROUP; + } + else if (FIRST_INT_GROUP <= gid && gid <= LAST_INT_GROUP) + { + group->type = INT_GROUP; + } + else + hw_abort (me, "internal error - unknown group id"); + } +} + + + +/* Perform the nasty work of figuring out which of the interrupt + groups should have its interrupt delivered. */ + +static int +find_highest_interrupt_group (struct hw *me, + struct mn103int *controller) +{ + int gid; + int selected; + + /* FIRST_NMI_GROUP (group zero) is used as a special default value + when searching for an interrupt group */ + selected = FIRST_NMI_GROUP; + controller->group[FIRST_NMI_GROUP].level = 7; + + for (gid = FIRST_INT_GROUP; gid <= LAST_INT_GROUP; gid++) + { + struct mn103int_group *group = &controller->group[gid]; + if ((group->request & group->enable) != 0) + { + if (group->level > controller->group[selected].level) + { + selected = gid; + } + } + } + return selected; +} + + +/* Notify the processor of an interrupt level update */ + +static void +push_interrupt_level (struct hw *me, + struct mn103int *controller) +{ + int selected = find_highest_interrupt_group (me, controller); + int level = controller->group[selected].level; + HW_TRACE ((me, "port-out - selected=%d level=%d", selected, level)); + hw_port_event (me, LEVEL_PORT, level, NULL, NULL_CIA); +} + + +/* An event arrives on an interrupt port */ + +static void +mn103int_port_event (struct hw *me, + int my_port, + struct hw *source, + int source_port, + int level, + sim_cpu *processor, + sim_cia cia) +{ + struct mn103int *controller = hw_data (me); + + switch (my_port) + { + + case ACK_PORT: + { + int selected = find_highest_interrupt_group (me, controller); + if (controller->group[selected].level != level) + hw_abort (me, "botched level synchronisation"); + controller->interrupt_accepted_group = selected; + HW_TRACE ((me, "port-event port=ack level=%d - selected=%d", + level, selected)); + break; + } + + default: + { + int gid; + int iid; + struct mn103int_group *group; + unsigned interrupt; + if (my_port > NR_G_PORTS) + hw_abort (me, "Event on unknown port %d", my_port); + + /* map the port onto an interrupt group */ + gid = (my_port % NR_G_PORTS) / 4; + group = &controller->group[gid]; + iid = (my_port % 4); + interrupt = 1 << iid; + + /* update our cached input */ + if (level) + group->input |= interrupt; + else + group->input &= ~interrupt; + + /* update the request bits */ + switch (group->trigger) + { + case ACTIVE_LOW: + case ACTIVE_HIGH: + if (level) + group->request |= interrupt; + break; + case NEGATIVE_EDGE: + case POSITIVE_EDGE: + group->request |= interrupt; + } + + /* force a corresponding output */ + switch (group->type) + { + + case NMI_GROUP: + { + /* for NMI's the event is the trigger */ + HW_TRACE ((me, "port-in port=%d group=%d interrupt=%d - NMI", + my_port, gid, iid)); + if ((group->request & group->enable) != 0) + { + HW_TRACE ((me, "port-out NMI")); + hw_port_event (me, NMI_PORT, 0, NULL, NULL_CIA); + } + break; + } + + case INT_GROUP: + { + /* if an interrupt is now pending */ + HW_TRACE ((me, "port-in port=%d group=%d interrupt=%d - INT", + my_port, gid, iid)); + push_interrupt_level (me, controller); + break; + } + } + break; + } + + } +} + +/* Read/write to to an ICR (group control register) */ + +static unsigned8 +read_icr (struct hw *me, + struct mn103int *controller, + struct mn103int_group *group, + unsigned_word offset) +{ + unsigned8 val = 0; + switch (group->type) + { + + case NMI_GROUP: + switch (offset) + { + case 0: + val = INSERT_ID (group->request); + HW_TRACE ((me, "read-icr 0x%02x", val)); + break; + } + break; + + case INT_GROUP: + switch (offset) + { + case 0: + val = (INSERT_IR (group->request) + | INSERT_ID (group->request & group->enable)); + HW_TRACE ((me, "read-icr 0:0x%02x", val)); + break; + case 1: + val = (INSERT_LV (group->level) + | INSERT_IE (group->enable)); + HW_TRACE ((me, "read-icr 1:0x%02x", val)); + break; + } + } + + return val; +} + +static void +write_icr (struct hw *me, + struct mn103int *controller, + struct mn103int_group *group, + unsigned_word offset, + unsigned8 val) +{ + switch (group->type) + { + + case NMI_GROUP: + switch (offset) + { + case 0: + HW_TRACE ((me, "write-icr 0x%02x", val)); + group->request &= ~EXTRACT_ID (val); + break; + default: + break; + } + break; + + case INT_GROUP: + switch (offset) + { + case 0: /* request/detect */ + /* Clear any ID bits and then set them according to IR */ + HW_TRACE ((me, "write-icr 0:0x%02x", val)); + group->request &= EXTRACT_ID (val); + group->request |= EXTRACT_IR (val) & EXTRACT_ID (val); + break; + case 1: /* level/enable */ + HW_TRACE ((me, "write-icr 1:0x%02x", val)); + group->level = EXTRACT_LV (val); + group->enable = EXTRACT_IE (val); + break; + default: + /* ignore */ + break; + } + push_interrupt_level (me, controller); + break; + + } +} + + +/* Read the IAGR (Interrupt accepted group register) */ + +static unsigned8 +read_iagr (struct hw *me, + struct mn103int *controller, + unsigned_word offset) +{ + unsigned8 val; + switch (offset) + { + case 0: + { + val = (controller->interrupt_accepted_group << 2); + if (!(controller->group[val].request + & controller->group[val].enable)) + /* oops, lost the request */ + val = 0; + break; + } + default: + val = 0; + } + return val; +} + + +/* Reads/writes to the EXTMD (external interrupt trigger configuration + register) */ + +static struct mn103int_group * +external_group (struct mn103int *controller, + unsigned_word offset) +{ + switch (offset) + { + case 0: + return &controller->group[16]; + case 1: + return &controller->group[20]; + default: + return NULL; + } +} + +static unsigned8 +read_extmd (struct hw *me, + struct mn103int *controller, + unsigned_word offset) +{ + int gid; + unsigned8 val = 0; + struct mn103int_group *group = external_group (controller, offset); + if (group != NULL) + { + for (gid = 0; gid < 4; gid++) + { + val |= (group[gid].trigger << (gid * 2)); + } + } + return val; +} + +static void +write_extmd (struct hw *me, + struct mn103int *controller, + unsigned_word offset, + unsigned8 val) +{ + int gid; + struct mn103int_group *group = external_group (controller, offset); + if (group != NULL) + { + for (gid = 0; gid < 4; gid++) + { + group[gid].trigger = (val >> (gid * 2)) & 0x3; + /* MAYBE: interrupts already pending? */ + } + } +} + + +/* generic read/write */ + +static int +decode_addr (struct hw *me, + struct mn103int *controller, + unsigned_word addr) +{ + int i; + for (i = 0; i < NR_BLOCKS; i++) + { + if (addr >= controller->block[i].base + && addr <= controller->block[i].bound) + return i; + } + hw_abort (me, "bad address"); + return -1; +} + +static struct mn103int_group * +decode_group (struct hw *me, + struct mn103int *controller, + unsigned_word addr) +{ + unsigned_word offset = (addr - controller->block[ICR_BLOCK].base); + int gid = (offset / 8) % NR_GROUPS; + return &controller->group[gid]; +} + +static unsigned +mn103int_io_read_buffer (struct hw *me, + void *dest, + int space, + unsigned_word base, + unsigned nr_bytes, + sim_cpu *processor, + sim_cia cia) +{ + struct mn103int *controller = hw_data (me); + unsigned8 *buf = dest; + unsigned byte; + for (byte = 0; byte < nr_bytes; byte++) + { + unsigned_word address = base + byte; + switch (decode_addr (me, controller, address)) + { + case ICR_BLOCK: + buf[byte] = read_icr (me, controller, + decode_group (me, controller, address), + address); + break; + case IAGR_BLOCK: + buf[byte] = read_iagr (me, controller, address); + break; + case EXTMD_BLOCK: + buf[byte] = read_extmd (me, controller, address); + break; + default: + hw_abort (me, "bad switch"); + } + } + return nr_bytes; +} + +static unsigned +mn103int_io_write_buffer (struct hw *me, + const void *source, + int space, + unsigned_word base, + unsigned nr_bytes, + sim_cpu *cpu, + sim_cia cia) +{ + struct mn103int *controller = hw_data (me); + const unsigned8 *buf = source; + unsigned byte; + for (byte = 0; byte < nr_bytes; byte++) + { + unsigned_word address = base + byte; + switch (decode_addr (me, controller, address)) + { + case ICR_BLOCK: + write_icr (me, controller, + decode_group (me, controller, address), + address, buf[byte]); + break; + case IAGR_BLOCK: + /* not allowed */ + break; + case EXTMD_BLOCK: + write_extmd (me, controller, address, buf[byte]); + break; + default: + hw_abort (me, "bad switch"); + } + } + return nr_bytes; +} + + +const struct hw_device_descriptor dv_mn103int_descriptor[] = { + { "mn103int", mn103int_finish, }, + { NULL }, +}; diff --git a/sim/mn10300/interp.c b/sim/mn10300/interp.c index eede9a706e3..4c14b70b95f 100644 --- a/sim/mn10300/interp.c +++ b/sim/mn10300/interp.c @@ -3,6 +3,7 @@ #if WITH_COMMON #include "sim-main.h" #include "sim-options.h" +#include "sim-hw.h" #else #include "mn10300_sim.h" #endif @@ -892,151 +893,6 @@ sim_load (sd, prog, abfd, from_tty) /* For compatibility */ SIM_DESC simulator; -/* mn10300 interrupt model */ - -enum interrupt_type -{ - int_reset, - int_nmi, - int_intov1, - int_intp10, - int_intp11, - int_intp12, - int_intp13, - int_intcm4, - num_int_types -}; - -char *interrupt_names[] = { - "reset", - "nmi", - "intov1", - "intp10", - "intp11", - "intp12", - "intp13", - "intcm4", - NULL -}; - - -static void -do_interrupt (sd, data) - SIM_DESC sd; - void *data; -{ -#if 0 - char **interrupt_name = (char**)data; - enum interrupt_type inttype; - inttype = (interrupt_name - STATE_WATCHPOINTS (sd)->interrupt_names); - - /* For a hardware reset, drop everything and jump to the start - address */ - if (inttype == int_reset) - { - PC = 0; - PSW = 0x20; - ECR = 0; - sim_engine_restart (sd, NULL, NULL, NULL_CIA); - } - - /* Deliver an NMI when allowed */ - if (inttype == int_nmi) - { - if (PSW & PSW_NP) - { - /* We're already working on an NMI, so this one must wait - around until the previous one is done. The processor - ignores subsequent NMIs, so we don't need to count them. - Just keep re-scheduling a single NMI until it manages to - be delivered */ - if (STATE_CPU (sd, 0)->pending_nmi != NULL) - sim_events_deschedule (sd, STATE_CPU (sd, 0)->pending_nmi); - STATE_CPU (sd, 0)->pending_nmi = - sim_events_schedule (sd, 1, do_interrupt, data); - return; - } - else - { - /* NMI can be delivered. Do not deschedule pending_nmi as - that, if still in the event queue, is a second NMI that - needs to be delivered later. */ - FEPC = PC; - FEPSW = PSW; - /* Set the FECC part of the ECR. */ - ECR &= 0x0000ffff; - ECR |= 0x10; - PSW |= PSW_NP; - PSW &= ~PSW_EP; - PSW |= PSW_ID; - PC = 0x10; - sim_engine_restart (sd, NULL, NULL, NULL_CIA); - } - } - - /* deliver maskable interrupt when allowed */ - if (inttype > int_nmi && inttype < num_int_types) - { - if ((PSW & PSW_NP) || (PSW & PSW_ID)) - { - /* Can't deliver this interrupt, reschedule it for later */ - sim_events_schedule (sd, 1, do_interrupt, data); - return; - } - else - { - /* save context */ - EIPC = PC; - EIPSW = PSW; - /* Disable further interrupts. */ - PSW |= PSW_ID; - /* Indicate that we're doing interrupt not exception processing. */ - PSW &= ~PSW_EP; - /* Clear the EICC part of the ECR, will set below. */ - ECR &= 0xffff0000; - switch (inttype) - { - case int_intov1: - PC = 0x80; - ECR |= 0x80; - break; - case int_intp10: - PC = 0x90; - ECR |= 0x90; - break; - case int_intp11: - PC = 0xa0; - ECR |= 0xa0; - break; - case int_intp12: - PC = 0xb0; - ECR |= 0xb0; - break; - case int_intp13: - PC = 0xc0; - ECR |= 0xc0; - break; - case int_intcm4: - PC = 0xd0; - ECR |= 0xd0; - break; - default: - /* Should never be possible. */ - sim_engine_abort (sd, NULL, NULL_CIA, - "do_interrupt - internal error - bad switch"); - break; - } - } - sim_engine_restart (sd, NULL, NULL, NULL_CIA); - } - - /* some other interrupt? */ - sim_engine_abort (sd, NULL, NULL_CIA, - "do_interrupt - internal error - interrupt %d unknown", - inttype); -#endif /* 0 */ -} - /* These default values correspond to expected usage for the chip. */ SIM_DESC @@ -1047,6 +903,7 @@ sim_open (kind, cb, abfd, argv) char **argv; { SIM_DESC sd = sim_state_alloc (kind, cb); + struct hw *hw; mn10300_callback = cb; SIM_ASSERT (STATE_MAGIC (sd) == SIM_MAGIC_NUMBER); @@ -1054,19 +911,20 @@ sim_open (kind, cb, abfd, argv) /* for compatibility */ simulator = sd; - /* FIXME: should be better way of setting up interrupts */ + /* FIXME: should be better way of setting up interrupts. For + moment, only support watchpoints causing a breakpoint (gdb + halt). */ STATE_WATCHPOINTS (sd)->pc = &(PC); STATE_WATCHPOINTS (sd)->sizeof_pc = sizeof (PC); - STATE_WATCHPOINTS (sd)->interrupt_handler = do_interrupt; - STATE_WATCHPOINTS (sd)->interrupt_names = interrupt_names; + STATE_WATCHPOINTS (sd)->interrupt_handler = NULL; + STATE_WATCHPOINTS (sd)->interrupt_names = NULL; if (sim_pre_argv_init (sd, argv[0]) != SIM_RC_OK) return 0; /* Allocate core managed memory */ - - /* "Mirror" the ROM addresses below 1MB. */ sim_do_command (sd, "memory region 0,0x100000"); + sim_do_command (sd, "memory region 0x40000000,0x100000"); /* getopt will print the error message so we just have to exit if this fails. FIXME: Hmmm... in the case of gdb we need getopt to call @@ -1079,6 +937,117 @@ sim_open (kind, cb, abfd, argv) return 0; } + hw = hw_tree_create (sd, "core"); + hw_tree_parse (hw, "/"); + if (STATE_VERBOSE_P (sd)) + hw_tree_parse (hw, "/trace? true"); + + + /* interrupt controller */ + + hw_tree_parse (hw, "/mn103int@0x34000100"); + if (STATE_VERBOSE_P (sd)) + hw_tree_parse (hw, "/mn103int/trace? true"); + hw_tree_parse (hw, "/mn103int/reg 0x34000100 0x68 0x34000200 0x8 0x3400280 0x8"); + + /* NMI input's */ + hw_tree_parse (hw, "/glue@0x30000000"); + if (STATE_VERBOSE_P (sd)) + hw_tree_parse (hw, "/glue@0x30000000/trace? true"); + hw_tree_parse (hw, "/glue@0x30000000/reg 0x30000000 16"); + hw_tree_parse (hw, "/glue@0x30000000 > int1 nmirq /mn103int"); + hw_tree_parse (hw, "/glue@0x30000000 > int2 watchdog /mn103int"); + hw_tree_parse (hw, "/glue@0x30000000 > int3 syserr /mn103int"); + + /* NMI output */ + hw_tree_parse (hw, "/mn103int > nmi int0 /glue@0x30000000"); + + /* ACK input */ + hw_tree_parse (hw, "/glue@0x30002000"); + if (STATE_VERBOSE_P (sd)) + hw_tree_parse (hw, "/glue@0x30002000/trace? true"); + hw_tree_parse (hw, "/glue@0x30002000/reg 0x30002000 4"); + hw_tree_parse (hw, "/glue@0x30002000 > int ack /mn103int"); + + /* LEVEL output */ + hw_tree_parse (hw, "/glue@0x30004000"); + if (STATE_VERBOSE_P (sd)) + hw_tree_parse (hw, "/glue@0x30004000/trace? true"); + hw_tree_parse (hw, "/glue@0x30004000/reg 0x30004000 4"); + hw_tree_parse (hw, "/mn103int > level int /glue@0x30004000"); + + /* A bunch of interrupt inputs */ + hw_tree_parse (hw, "/glue@0x30006000"); + if (STATE_VERBOSE_P (sd)) + hw_tree_parse (hw, "/glue@0x30006000/trace? true"); + hw_tree_parse (hw, "/glue@0x30006000/reg 0x30006000 16"); + hw_tree_parse (hw, "/glue@0x30006000 > int0 irq-0 /mn103int"); + hw_tree_parse (hw, "/glue@0x30006000 > int1 irq-1 /mn103int"); + hw_tree_parse (hw, "/glue@0x30006000 > int2 irq-2 /mn103int"); + hw_tree_parse (hw, "/glue@0x30006000 > int3 irq-3 /mn103int"); + + + /* processor interrupt device */ + + /* the device */ + hw_tree_parse (hw, "/mn103cpu@0x20000000"); + if (STATE_VERBOSE_P (sd)) + hw_tree_parse (hw, "/mn103cpu@0x20000000/trace? true"); + hw_tree_parse (hw, "/mn103cpu@0x20000000/reg 0x20000000 0x42"); + + /* DEBUG: ACK output wired upto a glue device */ + hw_tree_parse (hw, "/glue@0x20002000"); + if (STATE_VERBOSE_P (sd)) + hw_tree_parse (hw, "/glue@0x20002000/trace? true"); + hw_tree_parse (hw, "/glue@0x20002000/reg 0x20002000 4"); + hw_tree_parse (hw, "/mn103cpu > ack int0 /glue@0x20002000"); + + /* DEBUG: RESET/NMI/LEVEL wired up to a glue device */ + hw_tree_parse (hw, "/glue@0x20004000"); + if (STATE_VERBOSE_P (sd)) + hw_tree_parse (hw, "/glue@0x20004000/trace? true"); + hw_tree_parse (hw, "/glue@0x20004000/reg 0x20004000 12"); + hw_tree_parse (hw, "/glue@0x20004000 > int0 reset /mn103cpu"); + hw_tree_parse (hw, "/glue@0x20004000 > int1 nmi /mn103cpu"); + hw_tree_parse (hw, "/glue@0x20004000 > int2 level /mn103cpu"); + + /* The processor wired up to the real interrupt controller */ + hw_tree_parse (hw, "/mn103cpu > ack ack /mn103int"); + hw_tree_parse (hw, "/mn103int > level level /mn103cpu"); + hw_tree_parse (hw, "/mn103int > nmi nmi /mn103cpu"); + + + /* PAL */ + + /* the device */ + hw_tree_parse (hw, "/pal@0x31000000"); + if (STATE_VERBOSE_P (sd)) + hw_tree_parse (hw, "/pal@0x31000000/trace? true"); + hw_tree_parse (hw, "/pal@0x31000000/reg 0x31000000 64"); + + /* DEBUG: PAL wired up to a glue device */ + hw_tree_parse (hw, "/glue@0x31002000"); + if (STATE_VERBOSE_P (sd)) + hw_tree_parse (hw, "/glue@0x31002000/trace? true"); + hw_tree_parse (hw, "/glue@0x31002000/reg 0x31002000 16"); + hw_tree_parse (hw, "/pal@0x31000000 > countdown int0 /glue@0x31002000"); + hw_tree_parse (hw, "/pal@0x31000000 > timer int1 /glue@0x31002000"); + hw_tree_parse (hw, "/pal@0x31000000 > int int2 /glue@0x31002000"); + hw_tree_parse (hw, "/glue@0x31002000 > int0 int3 /glue@0x31002000"); + hw_tree_parse (hw, "/glue@0x31002000 > int1 int3 /glue@0x31002000"); + hw_tree_parse (hw, "/glue@0x31002000 > int2 int3 /glue@0x31002000"); + + /* The PAL wired up to the real interrupt controller */ + hw_tree_parse (hw, "/pal@0x31000000 > countdown irq-0 /mn103int"); + hw_tree_parse (hw, "/pal@0x31000000 > timer irq-1 /mn103int"); + hw_tree_parse (hw, "/pal@0x31000000 > int irq-2 /mn103int"); + + + + hw_tree_finish (hw); + if (STATE_VERBOSE_P (sd)) + hw_tree_print (hw); + /* check for/establish the a reference program image */ if (sim_analyze_program (sd, (STATE_PROG_ARGV (sd) != NULL