diff options
Diffstat (limited to 'sim')
-rw-r--r-- | sim/ChangeLog | 31 | ||||
-rw-r--r-- | sim/cris/Makefile.in | 19 | ||||
-rwxr-xr-x | sim/cris/configure | 330 | ||||
-rw-r--r-- | sim/cris/configure.ac | 5 | ||||
-rw-r--r-- | sim/cris/cris-sim.h | 10 | ||||
-rw-r--r-- | sim/cris/cris-tmpl.c | 15 | ||||
-rw-r--r-- | sim/cris/crisv10f.c | 61 | ||||
-rw-r--r-- | sim/cris/crisv32f.c | 70 | ||||
-rw-r--r-- | sim/cris/devices.c | 27 | ||||
-rw-r--r-- | sim/cris/dv-cris.c | 314 | ||||
-rw-r--r-- | sim/cris/dv-rv.c | 1221 | ||||
-rw-r--r-- | sim/cris/rvdummy.c | 536 | ||||
-rw-r--r-- | sim/cris/sim-if.c | 12 | ||||
-rw-r--r-- | sim/cris/sim-main.h | 11 |
14 files changed, 2649 insertions, 13 deletions
diff --git a/sim/ChangeLog b/sim/ChangeLog index 4d45bda70e4..144426a6aa6 100644 --- a/sim/ChangeLog +++ b/sim/ChangeLog @@ -1,3 +1,34 @@ +2006-04-03 Hans-Peter Nilsson <hp@axis.com> + + * cris/dv-cris.c, cris/dv-rv.c, cris/rvdummy.c: New files. + * cris/Makefile.in (CONFIG_DEVICES): Remove redundant setting. + (dv-cris.o, dv-rv.o rvdummy$(EXEEXT), rvdummy.o): New rules. + (all): Depend on rvdummy$(EXEEXT). + * cris/configure.ac: Call SIM_AC_OPTION_WARNINGS. Check for + sys/socket.h and sys/select.h. Call SIM_AC_OPTION_HARDWARE, + default off. + * cris/configure: Regenerate. + * cris/cris-sim.h (cris_have_900000xxif): Declare here. + (enum cris_interrupt_type, crisv10deliver_interrupt) + (crisv32deliver_interrupt: New declarations. + * cris/cris-tmpl.c [WITH_HW] (MY (f_model_insn_after)): Call + sim_events_tickn and set state-events member work_pending when it's + time for the next event. + [WITH_HW] (MY (f_specific_init)): Set CPU-model-specific + interrupt-delivery function. + * cris/crisv10f.c (MY (deliver_interrupt)): New function. + * cris/crisv32f.c (MY (deliver_interrupt)): New function. + * cris/devices.c: Include hw-device.h. + (device_io_read_buffer) [WITH_HW]: Call hw_io_read_buffer. + (device_io_write_buffer): Only perform 0x900000xx-functions if + cris_have_900000xxif is nonzero. Else if WITH_HW defined, + call hw_io_write_buffer. Add return 0 last in function. + * cris/sim-if.c (cris_have_900000xxif): Now global. + (sim_open) [WITH_HW]: Clear deliver_interrupt cpu member. + Force "-model" option, effectively. + * cris/sim-main.h (cris_interrupt_delivery_fn): New type. + (struct _sim_cpu) [WITH_HW]: New member deliver_interrupt. + 2006-04-02 Hans-Peter Nilsson <hp@axis.com> * cris/Makefile.in (CRISV10F_OBJS): Remove semcrisv10f-switch.o. diff --git a/sim/cris/Makefile.in b/sim/cris/Makefile.in index 313a0630dd6..e30740143e3 100644 --- a/sim/cris/Makefile.in +++ b/sim/cris/Makefile.in @@ -23,7 +23,6 @@ CRISV10F_OBJS = crisv10f.o cpuv10.o decodev10.o modelv10.o mloopv10f.o CRISV32F_OBJS = crisv32f.o cpuv32.o decodev32.o modelv32.o mloopv32f.o -CONFIG_DEVICES = dv-sockser.o CONFIG_DEVICES = SIM_OBJS = \ @@ -62,11 +61,29 @@ arch = cris sim-if.o: sim-if.c $(SIM_MAIN_DEPS) $(sim-core_h) $(sim-options_h) +# Needs CPU-specific knowledge. +dv-cris.o: dv-cris.c $(SIM_MAIN_DEPS) $(sim-core_h) + +# This is the same rule as dv-core.o etc. +dv-rv.o: dv-rv.c $(hw_main_headers) $(sim_main_headers) + arch.o: arch.c $(SIM_MAIN_DEPS) traps.o: traps.c targ-vals.h $(SIM_MAIN_DEPS) $(sim-options_h) devices.o: devices.c $(SIM_MAIN_DEPS) +# rvdummy is just used for testing. It does nothing if +# --enable-sim-hardware isn't active. + +all: rvdummy$(EXEEXT) + +check: rvdummy$(EXEEXT) + +rvdummy$(EXEEXT): rvdummy.o $(EXTRA_LIBDEPS) + $(CC) $(ALL_CFLAGS) -o rvdummy$(EXEEXT) rvdummy.o $(EXTRA_LIBS) + +rvdummy.o: rvdummy.c config.h tconfig.h $(remote_sim_h) $(callback_h) + # CRISV10 objs CRISV10F_INCLUDE_DEPS = \ diff --git a/sim/cris/configure b/sim/cris/configure index 253368c2ec7..b15e7dc6004 100755 --- a/sim/cris/configure +++ b/sim/cris/configure @@ -861,6 +861,9 @@ Optional Features: --enable-sim-alignment=align Specify strict, nonstrict or forced alignment of memory accesses. --enable-sim-hostendian=end Specify host byte endian orientation. --enable-sim-scache=size Specify simulator execution cache size. + --enable-build-warnings Enable build-time compiler warnings if gcc is used + --enable-gdb-build-warnings Enable SIM specific build-time compiler warnings if gcc is used + --enable-sim-hardware=LIST Specify the hardware to be included in the build. --enable-sim-default-model=model Specify default model to simulate. --enable-sim-environment=environment Specify mixed, user, virtual or operating environment. --enable-sim-inline=inlines Specify which functions should be inlined. @@ -7040,6 +7043,159 @@ sim_link_files="${sim_link_files} ${TARG_VALS_DEF}" sim_link_links="${sim_link_links} targ-vals.def" +# For dv-rv and rvdummy. + + +for ac_header in sys/socket.h sys/select.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------------ ## +## Report this to the AC_PACKAGE_NAME lists. ## +## ------------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + wire_alignment="NONSTRICT_ALIGNMENT" default_alignment="" @@ -7367,6 +7523,180 @@ else fi; +# NOTE: Don't add -Wall or -Wunused, they both include +# -Wunused-parameter which reports bogus warnings. +# NOTE: If you add to this list, remember to update +# gdb/doc/gdbint.texinfo. +build_warnings="-Wimplicit -Wreturn-type -Wcomment -Wtrigraphs \ +-Wformat -Wparentheses -Wpointer-arith" +# GCC supports -Wuninitialized only with -O or -On, n != 0. +if test x${CFLAGS+set} = xset; then + case "${CFLAGS}" in + *"-O0"* ) ;; + *"-O"* ) + build_warnings="${build_warnings} -Wuninitialized" + ;; + esac +else + build_warnings="${build_warnings} -Wuninitialized" +fi +# Up for debate: -Wswitch -Wcomment -trigraphs -Wtrigraphs +# -Wunused-function -Wunused-label -Wunused-variable -Wunused-value +# -Wchar-subscripts -Wtraditional -Wshadow -Wcast-qual +# -Wcast-align -Wwrite-strings -Wconversion -Wstrict-prototypes +# -Wmissing-prototypes -Wmissing-declarations -Wredundant-decls +# -Woverloaded-virtual -Winline -Werror" +# Check whether --enable-build-warnings or --disable-build-warnings was given. +if test "${enable_build_warnings+set}" = set; then + enableval="$enable_build_warnings" + case "${enableval}" in + yes) ;; + no) build_warnings="-w";; + ,*) t=`echo "${enableval}" | sed -e "s/,/ /g"` + build_warnings="${build_warnings} ${t}";; + *,) t=`echo "${enableval}" | sed -e "s/,/ /g"` + build_warnings="${t} ${build_warnings}";; + *) build_warnings=`echo "${enableval}" | sed -e "s/,/ /g"`;; +esac +if test x"$silent" != x"yes" && test x"$build_warnings" != x""; then + echo "Setting compiler warning flags = $build_warnings" 6>&1 +fi +fi; # Check whether --enable-sim-build-warnings or --disable-sim-build-warnings was given. +if test "${enable_sim_build_warnings+set}" = set; then + enableval="$enable_sim_build_warnings" + case "${enableval}" in + yes) ;; + no) build_warnings="-w";; + ,*) t=`echo "${enableval}" | sed -e "s/,/ /g"` + build_warnings="${build_warnings} ${t}";; + *,) t=`echo "${enableval}" | sed -e "s/,/ /g"` + build_warnings="${t} ${build_warnings}";; + *) build_warnings=`echo "${enableval}" | sed -e "s/,/ /g"`;; +esac +if test x"$silent" != x"yes" && test x"$build_warnings" != x""; then + echo "Setting GDB specific compiler warning flags = $build_warnings" 6>&1 +fi +fi; WARN_CFLAGS="" +WERROR_CFLAGS="" +if test "x${build_warnings}" != x -a "x$GCC" = xyes +then + echo "$as_me:$LINENO: checking compiler warning flags" >&5 +echo $ECHO_N "checking compiler warning flags... $ECHO_C" >&6 + # Separate out the -Werror flag as some files just cannot be + # compiled with it enabled. + for w in ${build_warnings}; do + case $w in + -Werr*) WERROR_CFLAGS=-Werror ;; + *) # Check that GCC accepts it + saved_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $w" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + WARN_CFLAGS="${WARN_CFLAGS} $w" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS="$saved_CFLAGS" + esac + done + echo "$as_me:$LINENO: result: ${WARN_CFLAGS}${WERROR_CFLAGS}" >&5 +echo "${ECHO_T}${WARN_CFLAGS}${WERROR_CFLAGS}" >&6 +fi + + +if test x"no" = x"yes"; then + sim_hw_p=yes +else + sim_hw_p=no +fi +if test ""; then + hardware="core pal glue" +else + hardware="core pal glue rv cris" +fi +sim_hw_cflags="-DWITH_HW=1" +sim_hw="$hardware" +sim_hw_objs="\$(SIM_COMMON_HW_OBJS) `echo $sim_hw | sed -e 's/\([^ ][^ ]*\)/dv-\1.o/g'`" +# 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) sim_hw_p=yes;; + no) sim_hw_p=no;; + ,*) sim_hw_p=yes; hardware="${hardware} `echo ${enableval} | sed -e 's/,/ /'`";; + *,) sim_hw_p=yes; hardware="`echo ${enableval} | sed -e 's/,/ /'` ${hardware}";; + *) sim_hw_p=yes; hardware="`echo ${enableval} | sed -e 's/,/ /'`"'';; +esac +if test "$sim_hw_p" != yes; then + sim_hw_objs= + sim_hw_cflags="-DWITH_HW=0" + sim_hw= +else + sim_hw_cflags="-DWITH_HW=1" + # remove duplicates + sim_hw="" + sim_hw_objs="\$(SIM_COMMON_HW_OBJS)" + for i in $hardware ; do + case " $sim_hw " in + *" $i "*) ;; + *) sim_hw="$sim_hw $i" ; sim_hw_objs="$sim_hw_objs dv-$i.o";; + esac + done +fi +if test x"$silent" != x"yes" && test "$sim_hw_p" = "yes"; then + echo "Setting hardware to $sim_hw_cflags, $sim_hw, $sim_hw_objs" +fi +else + +if test "$sim_hw_p" != yes; then + sim_hw_objs= + sim_hw_cflags="-DWITH_HW=0" + sim_hw= +fi +if test x"$silent" != x"yes"; then + echo "Setting hardware to $sim_hw_cflags, $sim_hw, $sim_hw_objs" +fi +fi; + # The default model shouldn't matter as long as there's a BFD. default_sim_default_model="crisv32" diff --git a/sim/cris/configure.ac b/sim/cris/configure.ac index a34a50bae8b..a349d8da945 100644 --- a/sim/cris/configure.ac +++ b/sim/cris/configure.ac @@ -9,9 +9,14 @@ sinclude(../common/aclocal.m4) # it by inlining the macro's contents. sinclude(../common/common.m4) +# For dv-rv and rvdummy. +AC_CHECK_HEADERS(sys/socket.h sys/select.h) + SIM_AC_OPTION_ALIGNMENT(NONSTRICT_ALIGNMENT) SIM_AC_OPTION_HOSTENDIAN SIM_AC_OPTION_SCACHE(16384) +SIM_AC_OPTION_WARNINGS +SIM_AC_OPTION_HARDWARE(no,,rv cris) # The default model shouldn't matter as long as there's a BFD. SIM_AC_OPTION_DEFAULT_MODEL(crisv32) diff --git a/sim/cris/cris-sim.h b/sim/cris/cris-sim.h index 66f736510f1..49a6f61e583 100644 --- a/sim/cris/cris-sim.h +++ b/sim/cris/cris-sim.h @@ -1,5 +1,5 @@ /* Collection of junk for CRIS. - Copyright (C) 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc. Contributed by Axis Communications. This file is part of the GNU simulators. @@ -86,6 +86,14 @@ extern USI crisv10f_break_handler (SIM_CPU *, USI, USI); extern USI crisv32f_break_handler (SIM_CPU *, USI, USI); extern USI cris_break_13_handler (SIM_CPU *, USI, USI, USI, USI, USI, USI, USI, USI); +extern char cris_have_900000xxif; +enum cris_interrupt_type { CRIS_INT_NMI, CRIS_INT_RESET, CRIS_INT_INT }; +extern int crisv10deliver_interrupt (SIM_CPU *, + enum cris_interrupt_type, + unsigned int); +extern int crisv32deliver_interrupt (SIM_CPU *, + enum cris_interrupt_type, + unsigned int); /* Using GNU syntax (not C99) so we can compile this on RH 6.2 (egcs-1.1.2/gcc-2.91.66). */ diff --git a/sim/cris/cris-tmpl.c b/sim/cris/cris-tmpl.c index 8994a11de69..81d1ca2de79 100644 --- a/sim/cris/cris-tmpl.c +++ b/sim/cris/cris-tmpl.c @@ -178,6 +178,18 @@ MY (f_model_insn_after) (SIM_CPU *current_cpu, int last_p ATTRIBUTE_UNUSED, PROFILE_MODEL_TOTAL_CYCLES (p) += cycles; CPU_CRIS_MISC_PROFILE (current_cpu)->basic_cycle_count += cycles; PROFILE_MODEL_CUR_INSN_CYCLES (p) = cycles; + +#if WITH_HW + /* For some reason, we don't get to the sim_events_tick call in + cgen-run.c:engine_run_1. Besides, more than one cycle has + passed, so we want sim_events_tickn anyway. The "events we want + to process" is usually to initiate an interrupt, but might also + be other events. We can't do the former until the main loop is + at point where it accepts changing the PC without internal + inconsistency, so just set a flag and wait. */ + if (sim_events_tickn (CPU_STATE (current_cpu), cycles)) + STATE_EVENTS (CPU_STATE (current_cpu))->work_pending = 1; +#endif } /* Initialize cycle counting for an insn. @@ -245,6 +257,9 @@ MY (f_specific_init) (SIM_CPU *current_cpu) { current_cpu->make_thread_cpu_data = MY (make_thread_cpu_data); current_cpu->thread_cpu_data_size = sizeof (current_cpu->cpu_data); +#if WITH_HW + current_cpu->deliver_interrupt = MY (deliver_interrupt); +#endif } /* Model function for arbitrary single stall cycles. */ diff --git a/sim/cris/crisv10f.c b/sim/cris/crisv10f.c index 0c2a17c801f..976badb5683 100644 --- a/sim/cris/crisv10f.c +++ b/sim/cris/crisv10f.c @@ -1,5 +1,5 @@ /* CRIS v10 simulator support code - Copyright (C) 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc. Contributed by Axis Communications. This file is part of the GNU simulators. @@ -40,3 +40,62 @@ MY (XCONCAT3 (f_model_crisv,BASENUM, } #endif /* WITH_PROFILE_MODEL_P */ + +/* Do the interrupt sequence if possible, and return 1. If interrupts + are disabled or some other lockout is active, return 0 and do + nothing. + + Beware, the v10 implementation is incomplete and doesn't properly + lock out interrupts e.g. after special-register access and doesn't + handle user-mode. */ + +int +MY (deliver_interrupt) (SIM_CPU *current_cpu, + enum cris_interrupt_type type, + unsigned int vec) +{ + unsigned char entryaddr_le[4]; + int was_user; + SIM_DESC sd = CPU_STATE (current_cpu); + unsigned32 entryaddr; + + /* We haven't implemented other interrupt-types yet. */ + if (type != CRIS_INT_INT) + abort (); + + /* We're supposed to be called outside of prefixes and branch + delay-slots etc, but why not check. */ + if (GET_H_INSN_PREFIXED_P ()) + abort (); + + if (!GET_H_IBIT ()) + return 0; + + /* User mode isn't supported for interrupts. (And we shouldn't see + this as 1 anyway. The user-mode bit isn't visible from user + mode. It doesn't make it into the U bit until the next + interrupt/exception.) */ + if (GET_H_UBIT ()) + abort (); + + SET_H_PBIT (1); + + if (sim_core_read_buffer (sd, + current_cpu, + read_map, entryaddr_le, + GET_H_SR (H_SR_PRE_V32_IBR) + vec * 4, 4) == 0) + { + /* Nothing to do actually; either abort or send a signal. */ + sim_core_signal (sd, current_cpu, CIA_GET (current_cpu), 0, 4, + GET_H_SR (H_SR_PRE_V32_IBR) + vec * 4, + read_transfer, sim_core_unmapped_signal); + return 0; + } + + entryaddr = bfd_getl32 (entryaddr_le); + + SET_H_SR (H_SR_PRE_V32_IRP, GET_H_PC ()); + SET_H_PC (entryaddr); + + return 1; +} diff --git a/sim/cris/crisv32f.c b/sim/cris/crisv32f.c index d1d5fc96e0e..cd14d3e3c67 100644 --- a/sim/cris/crisv32f.c +++ b/sim/cris/crisv32f.c @@ -1,5 +1,5 @@ /* CRIS v32 simulator support code - Copyright (C) 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc. Contributed by Axis Communications. This file is part of the GNU simulators. @@ -556,3 +556,71 @@ MY (XCONCAT3 (f_model_crisv,BASENUM, } #endif /* WITH_PROFILE_MODEL_P */ + +int +MY (deliver_interrupt) (SIM_CPU *current_cpu, + enum cris_interrupt_type type, + unsigned int vec) +{ + unsigned32 old_ccs, shifted_ccs, new_ccs; + unsigned char entryaddr_le[4]; + int was_user; + SIM_DESC sd = CPU_STATE (current_cpu); + unsigned32 entryaddr; + + /* We haven't implemented other interrupt-types yet. */ + if (type != CRIS_INT_INT) + abort (); + + /* We're called outside of branch delay slots etc, so we don't check + for that. */ + if (!GET_H_IBIT_V32 ()) + return 0; + + old_ccs = GET_H_SR_V32 (H_SR_CCS); + shifted_ccs = (old_ccs << 10) & ((1 << 30) - 1); + + /* The M bit is handled by code below and the M bit setter function, but + we need to preserve the Q bit. */ + new_ccs = shifted_ccs | (old_ccs & (unsigned32) 0x80000000UL); + was_user = GET_H_UBIT_V32 (); + + /* We need to force kernel mode since the setter method doesn't allow + it. Then we can use setter methods at will, since they then + recognize that we're in kernel mode. */ + CPU (h_ubit_v32) = 0; + + if (was_user) + { + /* These methods require that user mode is unset. */ + SET_H_SR (H_SR_USP, GET_H_GR (H_GR_SP)); + SET_H_GR (H_GR_SP, GET_H_KERNEL_SP ()); + } + + /* ERP setting is simplified by not taking interrupts in delay-slots + or when halting. */ + /* For all other exceptions than guru and NMI, store the return + address in ERP and set EXS and EXD here. */ + SET_H_SR (H_SR_ERP, GET_H_PC ()); + + /* Simplified by not having exception types (fault indications). */ + SET_H_SR_V32 (H_SR_EXS, (vec * 256)); + SET_H_SR_V32 (H_SR_EDA, 0); + + if (sim_core_read_buffer (sd, + current_cpu, + read_map, entryaddr_le, + GET_H_SR (H_SR_EBP) + vec * 4, 4) == 0) + { + /* Nothing to do actually; either abort or send a signal. */ + sim_core_signal (sd, current_cpu, CIA_GET (current_cpu), 0, 4, + GET_H_SR (H_SR_EBP) + vec * 4, + read_transfer, sim_core_unmapped_signal); + return 0; + } + + entryaddr = bfd_getl32 (entryaddr_le); + SET_H_PC (entryaddr); + + return 1; +} diff --git a/sim/cris/devices.c b/sim/cris/devices.c index feb901ae4ea..e694ff3168a 100644 --- a/sim/cris/devices.c +++ b/sim/cris/devices.c @@ -1,5 +1,5 @@ /* CRIS device support - Copyright (C) 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc. Contributed by Axis Communications. This file is part of the GNU simulators. @@ -27,6 +27,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "dv-sockser.h" #endif +#include "hw-device.h" + /* Placeholder definition. */ struct _device { char dummy; } cris_devices; @@ -48,7 +50,11 @@ device_io_read_buffer (device *me ATTRIBUTE_UNUSED, SIM_CPU *cpu ATTRIBUTE_UNUSED, sim_cia cia ATTRIBUTE_UNUSED) { +#if WITH_HW + return hw_io_read_buffer ((struct hw *) me, source, space, addr, nr_bytes); +#else abort (); +#endif } int @@ -61,13 +67,22 @@ device_io_write_buffer (device *me ATTRIBUTE_UNUSED, static const unsigned char ok[] = { 4, 0, 0, 0x90}; static const unsigned char bad[] = { 8, 0, 0, 0x90}; - if (addr == 0x90000004 && memcmp (source, ok, sizeof ok) == 0) - cris_break_13_handler (cpu, 1, 0, 0, 0, 0, 0, 0, cia); - else if (addr == 0x90000008 - && memcmp (source, bad, sizeof bad) == 0) - cris_break_13_handler (cpu, 1, 34, 0, 0, 0, 0, 0, cia); + if (cris_have_900000xxif) + { + if (addr == 0x90000004 && memcmp (source, ok, sizeof ok) == 0) + return cris_break_13_handler (cpu, 1, 0, 0, 0, 0, 0, 0, cia); + else if (addr == 0x90000008 + && memcmp (source, bad, sizeof bad) == 0) + return cris_break_13_handler (cpu, 1, 34, 0, 0, 0, 0, 0, cia); + } +#if WITH_HW + else + return hw_io_write_buffer ((struct hw *) me, source, space, addr, nr_bytes); +#endif /* If it wasn't one of those, send an invalid-memory signal. */ sim_core_signal (sd, cpu, cia, 0, nr_bytes, addr, write_transfer, sim_core_unmapped_signal); + + return 0; } diff --git a/sim/cris/dv-cris.c b/sim/cris/dv-cris.c new file mode 100644 index 00000000000..b98a6b73425 --- /dev/null +++ b/sim/cris/dv-cris.c @@ -0,0 +1,314 @@ +/* The CRIS interrupt framework for GDB, the GNU Debugger. + + Copyright 2006 Free Software Foundation, Inc. + + This file is part of GDB. + + 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 + + CRIS cpu virtual device (very rudimental; generic enough for all + currently used CRIS versions). + + + DESCRIPTION + + Implements the external CRIS functionality. This includes the + delivery of interrupts generated from other devices. + + + PROPERTIES + + vec-for-int = <int-a> <vec-a> <int-b> <vec-b> ... + These are the translations to interrupt vector for values appearing + on the "int" port, as pairs of the value and the corresponding + vector. Defaults to no translation. All values that may appear on + the "int" port must be defined, or the device aborts. + + multiple-int = ("abort" | "ignore_previous" | <vector>) + If multiple interrupt values are dispatched, this property decides + what to do. The value is either a number corresponding to the + vector to use, or the string "abort" to cause a hard abort, or the + string "ignore_previous", to silently use the new vector instead. + The default is "abort". + + + PORTS + + int (input) + Interrupt port. An event with a non-zero value on this port causes + an interrupt. If, after an event but before the interrupt has been + properly dispatched, a non-zero value appears that is different + after mapping than the previous, then the property multiple_int + decides what to do. + + FIXME: reg port so internal registers can be read. Requires + chip-specific versions, though. Ports "nmi" and "reset". + + + 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. + It also works around a bug in sim_events_process when doing so. + */ + +/* Keep this an enum for simple addition of "reset" and "nmi". */ +enum + { + INT_PORT, + }; + +static const struct hw_port_descriptor cris_ports[] = + { + { "int", INT_PORT, 0, input_port }, + { NULL, 0, 0, 0 } + }; + +struct cris_vec_tr + { + unsigned32 portval, vec; + }; + +enum cris_multiple_ints + { + cris_multint_abort, + cris_multint_ignore_previous, + cris_multint_vector + }; + +struct cris_hw + { + struct hw_event *pending_handler; + unsigned32 pending_vector; + struct cris_vec_tr *int_to_vec; + enum cris_multiple_ints multi_int_action; + unsigned32 multiple_int_vector; + }; + +/* An event function, calling the actual CPU-model-specific + interrupt-delivery function. */ + +static void +deliver_cris_interrupt (struct hw *me, void *data) +{ + struct cris_hw *crishw = hw_data (me); + SIM_DESC simulator = hw_system (me); + sim_cpu *cpu = STATE_CPU (simulator, 0); + unsigned int intno = crishw->pending_vector; + + if (CPU_CRIS_DELIVER_INTERRUPT (cpu) (cpu, CRIS_INT_INT, intno)) + { + crishw->pending_vector = 0; + crishw->pending_handler = NULL; + return; + } + + { + /* Bug workaround: at time T with a pending number of cycles N to + process, if re-scheduling an event at time T+M, M < N, + sim_events_process gets stuck at T (updating the "time" to + before the event rather than after the event, or somesuch). + + Hacking this locally is thankfully easy: if we see the same + simulation time, increase the number of cycles. Do this every + time we get here, until a new time is seen (supposedly unstuck + re-delivery). (Fixing in SIM/GDB source will hopefully then + also be easier, having a tangible test-case.) */ + static signed64 last_events_time = 0; + static signed64 delta = 1; + signed64 this_events_time = hw_event_queue_time (me); + + if (this_events_time == last_events_time) + delta++; + else + { + delta = 1; + last_events_time = this_events_time; + } + + crishw->pending_handler + = hw_event_queue_schedule (me, delta, deliver_cris_interrupt, NULL); + } +} + + +/* A port-event function for events arriving to an interrupt port. */ + +static void +cris_port_event (struct hw *me, + int my_port, + struct hw *source, + int source_port, + int intparam) +{ + struct cris_hw *crishw = hw_data (me); + unsigned32 vec; + + /* A few placeholders; only the INT port is implemented. */ + switch (my_port) + { + case INT_PORT: + HW_TRACE ((me, "INT value=0x%x", intparam)); + break; + + default: + hw_abort (me, "bad switch"); + break; + } + + if (intparam == 0) + return; + + if (crishw->int_to_vec != NULL) + { + unsigned int i; + for (i = 0; crishw->int_to_vec[i].portval != 0; i++) + if (crishw->int_to_vec[i].portval == intparam) + break; + + if (crishw->int_to_vec[i].portval == 0) + hw_abort (me, "unsupported value for int port: 0x%x", intparam); + + vec = crishw->int_to_vec[i].vec; + } + else + vec = (unsigned32) intparam; + + if (crishw->pending_vector != 0) + { + if (vec == crishw->pending_vector) + return; + + switch (crishw->multi_int_action) + { + case cris_multint_abort: + hw_abort (me, "int 0x%x (0x%x) while int 0x%x hasn't been delivered", + vec, intparam, crishw->pending_vector); + break; + + case cris_multint_ignore_previous: + break; + + case cris_multint_vector: + vec = crishw->multiple_int_vector; + break; + + default: + hw_abort (me, "bad switch"); + } + } + + crishw->pending_vector = vec; + + /* Schedule our event handler *now*. */ + if (crishw->pending_handler == NULL) + crishw->pending_handler + = hw_event_queue_schedule (me, 0, deliver_cris_interrupt, NULL); +} + +/* Instance initializer function. */ + +static void +cris_finish (struct hw *me) +{ + struct cris_hw *crishw; + const struct hw_property *vec_for_int; + const struct hw_property *multiple_int; + + crishw = HW_ZALLOC (me, struct cris_hw); + set_hw_data (me, crishw); + set_hw_ports (me, cris_ports); + set_hw_port_event (me, cris_port_event); + + vec_for_int = hw_find_property (me, "vec-for-int"); + if (vec_for_int != NULL) + { + unsigned32 vecsize; + unsigned32 i; + + if (hw_property_type (vec_for_int) != array_property) + hw_abort (me, "property \"vec-for-int\" has the wrong type"); + + vecsize = hw_property_sizeof_array (vec_for_int) / sizeof (signed_cell); + + if ((vecsize % 2) != 0) + hw_abort (me, "translation vector does not consist of even pairs"); + + crishw->int_to_vec + = hw_malloc (me, (vecsize/2 + 1) * sizeof (crishw->int_to_vec[0])); + + for (i = 0; i < vecsize/2; i++) + { + signed_cell portval_sc; + signed_cell vec_sc; + + if (!hw_find_integer_array_property (me, "vec-for-int", i*2, + &portval_sc) + || !hw_find_integer_array_property (me, "vec-for-int", i*2 + 1, + &vec_sc) + || portval_sc < 0 + || vec_sc < 0) + hw_abort (me, "no valid vector translation pair %u", i); + + crishw->int_to_vec[i].portval = (unsigned32) portval_sc; + crishw->int_to_vec[i].vec = (unsigned32) vec_sc; + } + + crishw->int_to_vec[i].portval = 0; + crishw->int_to_vec[i].vec = 0; + } + + multiple_int = hw_find_property (me, "multiple-int"); + if (multiple_int != NULL) + { + if (hw_property_type (multiple_int) == integer_property) + { + crishw->multiple_int_vector + = hw_find_integer_property (me, "multiple-int"); + crishw->multi_int_action = cris_multint_vector; + } + else + { + const char *action = hw_find_string_property (me, "multiple-int"); + + if (action == NULL) + hw_abort (me, "property \"multiple-int\" has the wrong type"); + + if (strcmp (action, "abort") == 0) + crishw->multi_int_action = cris_multint_abort; + else if (strcmp (action, "ignore_previous") == 0) + crishw->multi_int_action = cris_multint_ignore_previous; + else + hw_abort (me, "property \"multiple-int\" must be one of <vector number>\n" + "\"abort\" and \"ignore_previous\", not \"%s\"", action); + } + } + else + crishw->multi_int_action = cris_multint_abort; +} + +const struct hw_descriptor dv_cris_descriptor[] = { + { "cris", cris_finish, }, + { NULL }, +}; diff --git a/sim/cris/dv-rv.c b/sim/cris/dv-rv.c new file mode 100644 index 00000000000..30845e3e8e1 --- /dev/null +++ b/sim/cris/dv-rv.c @@ -0,0 +1,1221 @@ +/* The remote-virtual-component simulator framework + for GDB, the GNU Debugger. + + Copyright 2006 Free Software Foundation, Inc. + + This file is part of GDB. + + 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" + +#include "hw-tree.h" + +#include <ctype.h> + +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif + +#ifdef HAVE_STRING_H +#include <string.h> +#else +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif +#endif + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif + +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif + +#ifdef HAVE_SYS_TIME_H +#include <sys/time.h> +#endif + +#ifdef HAVE_SYS_SELECT_H +#include <sys/select.h> +#endif + +/* Not guarded in dv-sockser.c, so why here. */ +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <sys/socket.h> + + +/* DEVICE + + + rv - Remote Virtual component + + + DESCRIPTION + + + Socket connection to a remote simulator component, for example one + for testing a verilog construction. Protocol defined below. + + There is a set of 32-bit I/O ports, with a mapping from local to + remote addresses. There is a set of interrupts expressed as a + bit-mask, with a mapping from remote to local. There is a set of + memory ranges (actual memory defined elsewhere), also with a + mapping from remote to local addresses, that is expected to be + accessible to the remote simulator in 32-byte chunks (simulating + DMA). There is a mapping from remote cycles (or an appropriate + elsewhere defined time-slice) to local cycles. + + PROPERTIES + + reg = <address> <size> + The address (within the parent bus) that this device is to + be located. + + remote-reg = <remote-address> + The address of reg on the remote side. Defaults to 0. + + mem = <address> <size> + Specify an address-range (within the parent bus) that the remote + device can access. The memory is assumed to be already defined. + If there's no memory defined but the remote side asks for a memory + access, the simulation is aborted. + + remote-mem = <remote-address> + The address of mem on the remote side. Defaults to 0. + + mbox = <address> + Address of the mailbox interface. Writes to this address with the + local address of a mailbox command, a complete packet with length + and command; (4 or 6)) invokes the mailbox interface. Reads are + invalid. Replies are written to the same address. Address space + from <address> up-to-and-including <address>+3 is allocated. + + max-poll-ticks = <local-count> + Sets the maximum interval between polling the external component, + expressed in internal cycles. Defaults to 10000. + + watchdog-interval = <seconds> + Sets the wallclock seconds between watchdog packets sent to the + remote side (may be larger if there's no rv activity in that time). + Defaults to 30. If set to 0, no watchdog packets are sent. + + intnum = <local-int-0> <local-int-1> ... <local-int-31> + Defines a map from remote bit numbers to local values to be emitted + on the "int" port, with the external bit number as the ordinal - 1 + of the local translation. E.g. 43 121 would mean map external + (1<<0) to internal 43 and external (1<<1) to internal 121. The + default is unity; no translation. If more than one bit is set in + the remote interrupt word, the intmultiple property can be used to + control the translation. + + intmultiple = <intvalue> + When more than one bit is set in the remote interrupt word, you may + want to map this situation to a separate interrupt value. If this + property is non-zero, it is used as that value. If it is zero, the + local value for the "int" port is the bitwise-or of the translated + local values. + + host = <hostid> + The hostname or address where the simulator to be used listens. + Defaults to "127.0.0.1" + + port = <portnumber> + The hostname or address where the simulator to be used listens. + Defaults to 10000. + + dummy = <value> + or + dummy = <filename> + Don't connect to a remote side; use initial dummy contents from + <filename> (which has to be at least as big as the <size> argument + of reg above) or filled with byte-value <value>. Mailboxes are not + supported (can be defined but can not be used) and remote-memory + accesses don't apply. The main purpose for this property is to + simplify use of configuration and simulated hardware that is + e.g. only trivially initialized but not actually used. + + + PORTS + + int (output) + Driven as a result of a remote interrupt request. The value is a + 32-bit bitset of active interrupts. + + + BUGS + + All and none. + + + PROTOCOL + + This is version 1.0 of this protocol, defining packet format and + actions in a supposedly upward-compatible manner where client and + servers of different versions are expected to interoperate; the + format and the definitions below are hopefully generic enough to + allow this. + + Each connection has a server and a client (this code); the roles + are known beforehand. The client usually corresponds to a CPU and + memory system and the server corresponds to a memory-mapped + register hardware interface and/or a DMA controller. They + communicate using packets with specific commands, of which some + require replies from the other side; most are intiated by the + client with one exception. A reply uses the same format as the + command. + + Packets are at least three bytes long, where the first two bytes + form a header, a 16-bit little-endian number that is the total + length of the packet including the header. There is also a + one-byte command. The payload is optional, depending on the + command. + + [[16-bit-low-byte-of-length] [16-bit-high-byte-of-length] + [command/reply] [payload byte 0] [payload byte 1] + ... [payload byte (length-of-packet - 3)]] + + Commands: + + A client or server that reads an undocumented command may exit with + a hard error. Payload not defined or disallowed below is ignored. + + It is expected that future client versions find out the version of + the server side by polling with base commands, assuming earlier + versions if a certain reply isn't seen, with newly defined payload + parts where earlier versions left it undefined. New commands and + formats are sent only to the other side after the client and server + has found out each others version. Not all servers support all + commands; the type of server and supported set of commands is + expected to be known beforehand. + + RV_READ_CMD = 0 + Initiated by the client, requires a reply from the server. The + payload from the client is at least 4 bytes, forming a 4-byte + little-endian address, the rest being undefined. The reply from + the server is at least 8 bytes, forming the same address data as in + the request and the second 4-byte data being the little-endian + contents. + + RV_WRITE_CMD = 1 + Initiated by the client, requires a reply from the server. Payload + from the client is at least 8 bytes, forming a 4-byte little-endian + word being the address, the rest being the little-endian contents + to write. The reply from the server is 8 bytes unless elsewhere + agreed otherwise, forming the same address and data as in the + request. The data sent back may have been altered to correspond to + defined parts but can safely be discarded. + + RV_IRQ_CMD = 2 + Initiated by the server, no reply. The payload is 4 bytes, forming + a little-endian word with bits numbers corresponding to currently + active interrupt sources; value (1<<N) indicating interrupt source + N being active. + + RV_MEM_RD_CMD = 3 + Initiated by the server, requires a reply. A client must know + beforehand when (in command sequence or constant) the server can + send this command and if so must then not send any commands of its + own (including watchdog commands); the server is allowed to assume + that incoming data is only replies to this command. The format is + 8 bytes of data; 4 bytes of little-endian address followed by a + 32-bit little endian word with the number of bytes to read. The + reply is the same address and number of bytes, followed by the data + that had been read. + + RV_MEM_WR_CMD = 4 + Initiated by the server, no reply. The format is the same as a + reply to RV_MEM_RD_CMD; a 32-bit little-endian address, followed by + the 32-bit little-endian number of bytes to write (redundant + information but must be consistent with the packet header). + + RV_MBOX_HANDLE_CMD = 5 + Initiated by the client, requires a reply. The payload is 4 + undefined bytes followed by an binary blob, the size of the + blob given by the packet header. The reply is a 32-bit little + endian number at the same index as the undefined bytes. Actual + semantics are application-specific. + + RV_MBOX_PUT_CMD = 6 + Initiated by the client, requires a reply, with the reply using the + RV_MBOX_HANDLE_CMD reply format (i.e. *both* that command and + 32-bit little-endian number). The payload is a 32-bit little + endian number followed by an undefined payload, at most 20 bytes + long. The reply is a 32-bit little endian number. Actual + semantics are application-specific. + + RV_WATCHDOG_CMD = 7 + Initiated by the client, no reply. A version 1.0 client sends no + payload; a version 1.0 server should ignore any such payload. A + version 1.0 server must not send a reply. + + + Possible future enhancements: + + Synchronization; server and client reports the number of elapsed + cycles (unit to-be-defined) at each request or notification. + Pretty much the top-of-the-todo-list item. + + Large addresses; 1.0 being restricted to 32-bit addresses. + + Variable-size data; currently restricted to 32-bit register + accesses. + + Specified data endianness (not the packet header) perhaps as part + of an initial format request; currently little-endian only. + + + Usage notes: + When used with servers sending RV_MEM_RD_CMD but being + narrow-minded about indata, set watchdog-interval to 0. Use + multiple rv instances when there are e.g. separate register and + memory servers. Alway log, setting "/rv/trace? true", at the + development phase. Borrow from the test-suite. + */ + +#define RV_FAMILY_NAME "rv" + +enum rv_command { + RV_READ_CMD = 0, + RV_WRITE_CMD = 1, + RV_IRQ_CMD = 2, + RV_MEM_RD_CMD = 3, + RV_MEM_WR_CMD = 4, + RV_MBOX_HANDLE_CMD = 5, + RV_MBOX_PUT_CMD = 6, + RV_WATCHDOG_CMD = 7 +}; + + +typedef struct _hw_rv_device +{ + /* Mapping of remote interrupt bit-numbers to local ones. */ + unsigned32 remote_to_local_int[32]; + + /* When multiple bits are set, a non-zero value here indicates that + this value should be used instead. */ + unsigned32 intmultiple; + + /* Local address of registers. */ + unsigned32 reg_address; + + /* Size of register bank in bytes. */ + unsigned32 reg_size; + + /* Remote address of registers. */ + unsigned32 remote_reg_address; + + /* Local address of DMA:able memory. */ + unsigned32 mem_address; + + /* Size of DMA:able memory in bytes. */ + unsigned32 mem_size; + + /* Bitmask for valid DMA request size. */ + unsigned32 mem_burst_mask; + + /* Remote address of DMA:able memory. */ + unsigned32 remote_mem_address; + + /* (Local) address of mbox; where to put a pointer to the mbox to be + sent. */ + unsigned32 mbox_address; + + /* Probably not 127.0.0.1:10000. */ + const char *host; + int port; + + /* If non-NULL, points to memory to use instead of connection. */ + unsigned8 *dummy; + + /* File descriptor for the socket. Set to -1 when error. Only one + of dummy and this is active. */ + int fd; + + /* Stashed errno, as we don't emit an error right away. */ + int saved_errno; + + /* This, plus latency because the CPU might not be checking until a + CTI insn (usually a branch or a jump) is the interval in cycles + between the rv is polled for e.g. DMA requests. */ + unsigned32 max_tick_poll_interval; + + /* Running counter for exponential backoff up to + max_tick_poll_interval to avoid polling the connection + unnecessarily often. Set to 1 when rv activity (read/write + register, DMA request) is detected. */ + unsigned32 next_period; + + /* This is the interval in wall-clock seconds between watchdog + packets are sent to the remote side. Zero means no watchdog + packets. */ + unsigned32 watchdog_interval; + + /* Last time we sent a watchdog packet. */ + struct timeval last_wdog_time; + + /* Mostly used as a kludge for knowing which rv:s have poll events + active. */ + struct hw_event *poll_callback; +} hw_rv_device; + + +/* We might add ports in the future, so keep this an enumeration. */ +enum + { + INT_PORT + }; + +/* Our ports. */ +static const struct hw_port_descriptor hw_rv_ports[] = { + { "int", INT_PORT, 0, output_port }, + { NULL } +}; + +/* Send LEN bytes of data from BUF to the socket. Abort on + errors. */ + +static void +hw_rv_write (struct hw *me, + void *buf, + unsigned int len) +{ + hw_rv_device *rv = (hw_rv_device *) hw_data (me); + unsigned8 *bufp = buf; + + /* If we don't have a valid fd here, it's because we got an error + initially, and we suppressed that error. */ + if (rv->fd < 0) + hw_abort (me, "couldn't open a connection to %s:%d because: %s", + rv->host, rv->port, strerror (rv->saved_errno)); + + while (len > 0) + { + ssize_t ret = write (rv->fd, bufp, len); + if (ret < 0) + /* FIXME: More graceful exit. */ + hw_abort (me, "write to %s:%d failed: %s\n", rv->host, rv->port, + strerror (errno)); + + len -= ret; + bufp += ret; + } +} + +/* Read LEN bytes of data into BUF from the socket. Set the file + descriptor to -1 if there's an error. */ + +static void +hw_rv_read (struct hw *me, + void *buf, + unsigned int len) +{ + hw_rv_device *rv = (hw_rv_device *) hw_data (me); + unsigned8 *bufp = buf; + + while (len > 0) + { + ssize_t ret = read (rv->fd, bufp, len); + + /* We get all zero if the remote end quits, but no error + indication; even select says there's data active. */ + if (ret <= 0) + { + if (close (rv->fd) != 0) + /* FIXME: More graceful exit. */ + hw_abort (me, "read from %s:%d failed: %d\n", rv->host, rv->port, errno); + rv->fd = -1; + return; + } + + len -= ret; + bufp += ret; + } +} + +/* Construct and send a packet of data of type CMD and len + LEN_NOHEADER (not counting the header...). */ + +static void +hw_rv_send (struct hw *me, + unsigned int cmd, + void *msg, + unsigned int len_noheader) +{ + hw_rv_device *rv = (hw_rv_device *) hw_data (me); + unsigned8 buf[32+3]; + unsigned8 *bufp; + unsigned int len = len_noheader + 3; + int ret; + + buf[0] = len & 255; + buf[1] = (len >> 8) & 255; + buf[2] = cmd; + + if (len > sizeof (buf)) + { + hw_rv_write (me, buf, 3); + len = len_noheader; + bufp = msg; + } + else + { + memcpy (buf + 3, msg, len_noheader); + bufp = buf; + } + + hw_rv_write (me, bufp, len); +} + +/* Handle incoming DMA requests as per the RV_MEM_RD_CMD packet. + Abort on errors. */ + +static void +hw_rv_read_mem (struct hw *me, unsigned int len) +{ + hw_rv_device *rv = (hw_rv_device *) hw_data (me); + /* If you change this size, please adjust the mem2 testcase. */ + unsigned8 buf[32+8]; + unsigned8 *bufp = buf; + unsigned32 leaddr; + unsigned32 addr; + unsigned32 lelen; + unsigned32 i; + + if (len != 8) + hw_abort (me, "expected DMA read request len 8+3, got %d+3", len); + + hw_rv_read (me, &leaddr, 4); + hw_rv_read (me, &lelen, 4); + len = LE2H_4 (lelen); + addr = LE2H_4 (leaddr); + + if (addr < rv->remote_mem_address + || addr >= rv->remote_mem_address + rv->mem_size) + hw_abort (me, "DMA read at remote 0x%x; outside [0x%x..0x%x-1]", + (unsigned) addr, (unsigned) rv->remote_mem_address, + (unsigned) (rv->remote_mem_address + rv->mem_size)); + addr = addr - rv->remote_mem_address + rv->mem_address; + + if (len == 0) + hw_abort (me, "DMA read request for 0 bytes isn't supported"); + + if (len & ~rv->mem_burst_mask) + hw_abort (me, "DMA trying to read %d bytes; not matching mask of 0x%x", + len, rv->mem_burst_mask); + if (len + 8 > sizeof (buf)) + bufp = hw_malloc (me, len + 8); + + HW_TRACE ((me, "DMA R 0x%x..0x%x", addr, addr + len -1)); + hw_dma_read_buffer (me, bufp + 8, 0, addr, len); + if (hw_trace_p (me)) + for (i = 0; i < len; i += 4) + HW_TRACE ((me, "0x%x: %02x %02x %02x %02x", + addr + i, + bufp[i+8], bufp[i+9], bufp[i+10], bufp[i+11])); + + memcpy (bufp, &leaddr, 4); + memcpy (bufp + 4, &lelen, 4); + hw_rv_send (me, RV_MEM_RD_CMD, bufp, len + 8); + if (bufp != buf) + hw_free (me, bufp); +} + +/* Handle incoming DMA requests as per the RV_MEM_WR_CMD packet. + Abort on errors. */ + +static void +hw_rv_write_mem (struct hw *me, unsigned int plen) +{ + hw_rv_device *rv = (hw_rv_device *) hw_data (me); + /* If you change this size, please adjust the mem2 testcase. */ + unsigned8 buf[32+8]; + unsigned8 *bufp = buf; + unsigned32 leaddr; + unsigned32 addr; + unsigned32 lelen; + unsigned32 len; + unsigned32 i; + + hw_rv_read (me, &leaddr, 4); + hw_rv_read (me, &lelen, 4); + len = LE2H_4 (lelen); + addr = LE2H_4 (leaddr); + + if (len != plen - 8) + hw_abort (me, + "inconsistency in DMA write request packet: " + "envelope %d+3, inner %d bytes", plen, len); + + if (addr < rv->remote_mem_address + || addr >= rv->remote_mem_address + rv->mem_size) + hw_abort (me, "DMA write at remote 0x%x; outside [0x%x..0x%x-1]", + (unsigned) addr, (unsigned) rv->remote_mem_address, + (unsigned) (rv->remote_mem_address + rv->mem_size)); + + addr = addr - rv->remote_mem_address + rv->mem_address; + if (len == 0) + hw_abort (me, "DMA write request for 0 bytes isn't supported"); + + if (len & ~rv->mem_burst_mask) + hw_abort (me, "DMA trying to write %d bytes; not matching mask of 0x%x", + len, rv->mem_burst_mask); + if (len + 8 > sizeof (buf)) + bufp = hw_malloc (me, len + 8); + + hw_rv_read (me, bufp + 8, len); + HW_TRACE ((me, "DMA W 0x%x..0x%x", addr, addr + len - 1)); + hw_dma_write_buffer (me, bufp + 8, 0, addr, len, 0); + if (hw_trace_p (me)) + for (i = 0; i < len; i += 4) + HW_TRACE ((me, "0x%x: %02x %02x %02x %02x", + addr + i, + bufp[i+8], bufp[i+9], bufp[i+10], bufp[i+11])); + if (bufp != buf) + hw_free (me, bufp); +} + +static void +hw_rv_irq (struct hw *me, unsigned int len) +{ + hw_rv_device *rv = (hw_rv_device *) hw_data (me); + unsigned32 intbitsle; + unsigned32 intbits_ext; + unsigned32 intval = 0; + int i; + + if (len != 4) + hw_abort (me, "IRQ with %d data not supported", len); + + hw_rv_read (me, &intbitsle, 4); + intbits_ext = LE2H_4 (intbitsle); + for (i = 0; i < 32; i++) + if ((intbits_ext & (1 << i)) != 0) + intval |= rv->remote_to_local_int[i]; + if ((intbits_ext & ~(intbits_ext - 1)) != intbits_ext + && rv->intmultiple != 0) + intval = rv->intmultiple; + + HW_TRACE ((me, "IRQ 0x%x", intval)); + hw_port_event (me, INT_PORT, intval); +} + +/* Handle incoming interrupt notifications as per the RV_IRQ_CMD + packet. Abort on errors. */ + +static void +hw_rv_handle_incoming (struct hw *me, + int expected_type, + unsigned8 *buf, + unsigned int *return_len) +{ + hw_rv_device *rv = (hw_rv_device *) hw_data (me); + unsigned8 cbuf[32]; + unsigned int len; + unsigned int cmd; + + while (1) + { + hw_rv_read (me, cbuf, 3); + + if (rv->fd < 0) + return; + + len = cbuf[0] + cbuf[1] * 256 - 3; + cmd = cbuf[2]; + + /* These come in "asynchronously"; not as a reply. */ + switch (cmd) + { + case RV_IRQ_CMD: + hw_rv_irq (me, len); + break; + + case RV_MEM_RD_CMD: + hw_rv_read_mem (me, len); + break; + + case RV_MEM_WR_CMD: + hw_rv_write_mem (me, len); + break; + } + + /* Something is incoming from the other side, so tighten up all + slack at the next wait. */ + rv->next_period = 1; + + switch (cmd) + { + case RV_MEM_RD_CMD: + case RV_MEM_WR_CMD: + case RV_IRQ_CMD: + /* Don't try to handle more than one of these if we were'nt + expecting a reply. */ + if (expected_type == -1) + return; + continue; + } + + /* Require a match between this supposed-reply and the command + for the rest. */ + if (cmd != expected_type) + hw_abort (me, "unexpected reply, expected command %d, got %d", + expected_type, cmd); + + switch (cmd) + { + case RV_MBOX_PUT_CMD: + case RV_MBOX_HANDLE_CMD: + case RV_WRITE_CMD: + case RV_READ_CMD: + hw_rv_read (me, buf, len <= *return_len ? len : *return_len); + *return_len = len; + break; + } + break; + } +} + +/* Send a watchdog packet. Make a note of wallclock time. */ + +static void +hw_rv_send_wdog (struct hw *me) +{ + hw_rv_device *rv = (hw_rv_device *) hw_data (me); + HW_TRACE ((me, "WD")); + gettimeofday (&rv->last_wdog_time, NULL); + hw_rv_send (me, RV_WATCHDOG_CMD, "", 0); +} + +/* Poll the remote side: see if there's any incoming traffic; handle a + packet if so. Send a watchdog packet if it's time to do so. + Beware that the Linux select call indicates traffic for a socket + that the remote side has closed (which may be because it was + finished; don't hork until we need to write something just because + we're polling). */ + +static void +hw_rv_poll_once (struct hw *me) +{ + hw_rv_device *rv = (hw_rv_device *) hw_data (me); + fd_set rfds; + fd_set efds; + struct timeval now; + int ret; + struct timeval tv; + + if (rv->fd < 0) + /* Connection has died or was never initiated. */ + return; + + FD_ZERO (&rfds); + FD_SET (rv->fd, &rfds); + FD_ZERO (&efds); + FD_SET (rv->fd, &efds); + tv.tv_sec = 0; + tv.tv_usec = 0; + + ret = select (rv->fd + 1, &rfds, NULL, &efds, &tv); + gettimeofday (&now, NULL); + + if (ret < 0) + hw_abort (me, "select failed: %d\n", errno); + + if (rv->watchdog_interval != 0 + && now.tv_sec - rv->last_wdog_time.tv_sec >= rv->watchdog_interval) + hw_rv_send_wdog (me); + + if (FD_ISSET (rv->fd, &rfds)) + hw_rv_handle_incoming (me, -1, NULL, NULL); +} + +/* Initialize mapping of remote-to-local interrupt data. */ + +static void +hw_rv_map_ints (struct hw *me) +{ + hw_rv_device *rv = (hw_rv_device *) hw_data (me); + int i; + + for (i = 0; i < 32; i++) + rv->remote_to_local_int[i] = 1 << i; + + if (hw_find_property (me, "intnum") != NULL) + for (i = 0; i < 32; i++) + { + signed_cell val = -1; + if (hw_find_integer_array_property (me, "intnum", i, &val) > 0) + { + if (val > 0) + rv->remote_to_local_int[i] = val; + else + hw_abort (me, "property \"intnum@%d\" must be > 0; is %d", + i, (int) val); + } + } +} + +/* Handle the after-N-ticks "poll event", calling the poll-the-fd + method. Update the period. */ + +static void +do_poll_event (struct hw *me, void *data) +{ + hw_rv_device *rv = (hw_rv_device *) hw_data (me); + unsigned32 new_period; + + if (rv->dummy != NULL) + return; + + hw_rv_poll_once (me); + if (rv->fd >= 0) + rv->poll_callback + = hw_event_queue_schedule (me, rv->next_period, do_poll_event, NULL); + + new_period = rv->next_period * 2; + if (new_period <= rv->max_tick_poll_interval) + rv->next_period = new_period; +} + +/* HW tree traverse function for hw_rv_add_init. */ + +static void +hw_rv_add_poller (struct hw *me, void *data) +{ + hw_rv_device *rv; + + if (hw_family (me) == NULL + || strcmp (hw_family (me), RV_FAMILY_NAME) != 0) + return; + + rv = (hw_rv_device *) hw_data (me); + if (rv->poll_callback != NULL) + return; + + rv->poll_callback + = hw_event_queue_schedule (me, 1, do_poll_event, NULL); +} + +/* Simulator module init function for hw_rv_add_init. */ + +/* FIXME: For the call so hw_tree_traverse, we need to know that the + first member of struct sim_hw is the struct hw *root, but there's + no accessor method and struct sim_hw is defined in sim-hw.c only. + Hence this hack, until an accessor is added, or there's a traverse + function that takes a SIM_DESC argument. */ +struct sim_hw { struct hw *tree; }; + +static SIM_RC +hw_rv_add_rv_pollers (SIM_DESC sd) +{ + hw_tree_traverse (STATE_HW (sd)->tree, hw_rv_add_poller, NULL, NULL); + return SIM_RC_OK; +} + +/* We need to add events for polling, but we can't add one from the + finish-function, and there are no other call points, at least for + instances without "reg" (when there are just DMA requests from the + remote end; no locally initiated activity). Therefore we add a + simulator module init function, but those don't have private + payload arguments; just a SD argument. We cope by parsing the HW + root and making sure *all* "rv":s have poll callbacks installed. + Luckily, this is just an initialization step, and not many + simultaneous instances of rv are expected: we get a N**2 complexity + for visits to each rv node by this method. */ + +static void +hw_rv_add_init (struct hw *me) +{ + sim_module_add_init_fn (hw_system (me), hw_rv_add_rv_pollers); +} + +/* Open up a connection to the other side. Abort on errors. */ + +static void +hw_rv_init_socket (struct hw *me) +{ + hw_rv_device *rv = (hw_rv_device *) hw_data (me); + int sock; + struct sockaddr_in server; + + rv->fd = -1; + + if (rv->dummy != NULL) + return; + + memset (&server, 0, sizeof (server)); + server.sin_family = AF_INET; + server.sin_addr.s_addr = inet_addr (rv->host); + + /* Solaris 2.7 lacks this macro. */ +#ifndef INADDR_NONE +#define INADDR_NONE -1 +#endif + + if (server.sin_addr.s_addr == INADDR_NONE) + { + struct hostent *h; + h = gethostbyname (rv->host); + if (h != NULL) + { + memcpy (&server.sin_addr, h->h_addr, h->h_length); + server.sin_family = h->h_addrtype; + } + else + hw_abort (me, "can't resolve host %s", rv->host); + } + + server.sin_port = htons (rv->port); + sock = socket (AF_INET, SOCK_STREAM, 0); + + if (sock < 0) + hw_abort (me, "can't get a socket for %s:%d connection", + rv->host, rv->port); + + if (connect (sock, (struct sockaddr *) &server, sizeof server) >= 0) + { + rv->fd = sock; + + /* FIXME: init packet here. Maybe start packet too. */ + if (rv->watchdog_interval != 0) + hw_rv_send_wdog (me); + } + else + /* Stash the errno for later display, if some connection activity + is requested. Don't emit an error here; we might have been + called just for test purposes. */ + rv->saved_errno = errno; +} + +/* Local rv register reads end up here. */ + +static unsigned int +hw_rv_reg_read (struct hw *me, + void *dest, + int space, + unsigned_word addr, + unsigned int nr_bytes) +{ + hw_rv_device *rv = (hw_rv_device *) hw_data (me); + unsigned8 addr_data[8] = ""; + unsigned32 a_l = H2LE_4 (addr - rv->reg_address + rv->remote_reg_address); + unsigned int len = 8; + + if (nr_bytes != 4) + hw_abort (me, "must be four byte read"); + + if (addr == rv->mbox_address) + hw_abort (me, "invalid read of mbox address 0x%x", + (unsigned) rv->mbox_address); + + memcpy (addr_data, &a_l, 4); + HW_TRACE ((me, "REG R 0x%x", addr)); + if (rv->dummy != NULL) + { + len = 8; + memcpy (addr_data + 4, rv->dummy + addr - rv->reg_address, 4); + } + else + { + hw_rv_send (me, RV_READ_CMD, addr_data, len); + hw_rv_handle_incoming (me, RV_READ_CMD, addr_data, &len); + } + + if (len != 8) + hw_abort (me, "read %d != 8 bytes returned", len); + HW_TRACE ((me, ":= 0x%02x%02x%02x%02x", + addr_data[7], addr_data[6], addr_data[5], addr_data[4])); + memcpy (dest, addr_data + 4, 4); + return nr_bytes; +} + +/* Local rv mbox requests (handle or put) end up here. */ + +static void +hw_rv_mbox (struct hw *me, unsigned_word address) +{ + unsigned8 buf[256+3]; + unsigned int cmd; + unsigned int rlen; + unsigned32 i; + unsigned int len + = hw_dma_read_buffer (me, buf, 0, address, 3); + + if (len != 3) + hw_abort (me, "mbox read %d != 3 bytes returned", len); + + cmd = buf[2]; + if (cmd != RV_MBOX_HANDLE_CMD && cmd != RV_MBOX_PUT_CMD) + hw_abort (me, "unsupported mbox command %d", cmd); + + len = buf[0] + buf[1]*256; + + if (len > sizeof (buf)) + hw_abort (me, "mbox cmd %d send size %d unsupported", cmd, len); + + rlen = hw_dma_read_buffer (me, buf + 3, 0, address + 3, len - 3); + if (rlen != len - 3) + hw_abort (me, "mbox read %d != %d bytes returned", rlen, len - 3); + + HW_TRACE ((me, "MBOX %s 0x%x..0x%x", + cmd == RV_MBOX_HANDLE_CMD ? "H" : "P", + address, address + len - 1)); + for (i = 0; i < rlen; i += 8) + HW_TRACE ((me, "0x%x: %02x %02x %02x %02x %02x %02x %02x %02x", + address + 3 + i, + buf[3+i], buf[4+i], buf[5+i], buf[6+i], buf[7+i], buf[8+i], + buf[9+i], buf[10+i])); + + len -= 3; + hw_rv_send (me, cmd, buf + 3, len); + + /* Note: both ..._PUT and ..._HANDLE get the ..._HANDLE reply. */ + hw_rv_handle_incoming (me, RV_MBOX_HANDLE_CMD, buf + 3, &len); + if (len > sizeof (buf)) + hw_abort (me, "mbox cmd %d receive size %d unsupported", cmd, len); + HW_TRACE ((me, "-> 0x%x..0x%x", address, address + len + 3 - 1)); + for (i = 0; i < len; i += 8) + HW_TRACE ((me, "0x%x: %02x %02x %02x %02x %02x %02x %02x %02x", + address + 3 + i, + buf[3+i], buf[4+i], buf[5+i], buf[6+i], buf[7+i], buf[8+i], + buf[9+i], buf[10+i])); + + len += 3; + buf[0] = len & 255; + buf[1] = len / 256; + rlen = hw_dma_write_buffer (me, buf, 0, address, len, 0); + if (rlen != len) + hw_abort (me, "mbox write %d != %d bytes", rlen, len); +} + +/* Local rv register writes end up here. */ + +static unsigned int +hw_rv_reg_write (struct hw *me, + const void *source, + int space, + unsigned_word addr, + unsigned int nr_bytes) +{ + hw_rv_device *rv = (hw_rv_device *) hw_data (me); + + unsigned8 addr_data[8] = ""; + unsigned32 a_l = H2LE_4 (addr - rv->reg_address + rv->remote_reg_address); + unsigned int len = 8; + + if (nr_bytes != 4) + hw_abort (me, "must be four byte write"); + + memcpy (addr_data, &a_l, 4); + memcpy (addr_data + 4, source, 4); + + if (addr == rv->mbox_address) + { + unsigned32 mbox_addr_le; + if (rv->dummy != NULL) + hw_abort (me, "mbox not supported for a dummy instance"); + memcpy (&mbox_addr_le, source, 4); + hw_rv_mbox (me, LE2H_4 (mbox_addr_le)); + return nr_bytes; + } + + HW_TRACE ((me, "REG W 0x%x := 0x%02x%02x%02x%02x", addr, + addr_data[7], addr_data[6], addr_data[5], addr_data[4])); + if (rv->dummy != NULL) + { + len = 8; + memcpy (rv->dummy + addr - rv->reg_address, addr_data + 4, 4); + } + else + { + hw_rv_send (me, RV_WRITE_CMD, addr_data, len); + hw_rv_handle_incoming (me, RV_WRITE_CMD, addr_data, &len); + } + + if (len != 8) + hw_abort (me, "read %d != 8 bytes returned", len); + + /* We had an access: tighten up all slack. */ + rv->next_period = 1; + + return nr_bytes; +} + +/* Instance initializer function. */ + +static void +hw_rv_finish (struct hw *me) +{ + hw_rv_device *rv = HW_ZALLOC (me, hw_rv_device); + int i; + const struct hw_property *mem_prop; + const struct hw_property *dummy_prop; + const struct hw_property *mbox_prop; + + set_hw_data (me, rv); + +#undef RV_GET_IPROP +#undef RV_GET_PROP +#define RV_GET_PROP(T, N, M, D) \ + do \ + { \ + if (hw_find_property (me, N) != NULL) \ + rv->M = hw_find_ ## T ## _property (me, N); \ + else \ + rv->M = (D); \ + } \ + while (0) +#define RV_GET_IPROP(N, M, D) RV_GET_PROP (integer, N, M, D) + + RV_GET_PROP (string, "host", host, "127.0.0.1"); + RV_GET_IPROP ("port", port, 10000); + RV_GET_IPROP ("remote-reg", remote_reg_address, 0); + RV_GET_IPROP ("max-poll-ticks", max_tick_poll_interval, 10000); + RV_GET_IPROP ("watchdog-interval", watchdog_interval, 30); + RV_GET_IPROP ("remote-mem", remote_mem_address, 0); + RV_GET_IPROP ("mem-burst-mask", mem_burst_mask, 0xffff); + RV_GET_IPROP ("intmultiple", intmultiple, 0); + + set_hw_io_read_buffer (me, hw_rv_reg_read); + set_hw_io_write_buffer (me, hw_rv_reg_write); + set_hw_ports (me, hw_rv_ports); + rv->next_period = 1; + + /* FIXME: We only support zero or one reg and zero or one mem area. */ + if (hw_find_property (me, "reg") != NULL) + { + reg_property_spec reg; + if (hw_find_reg_array_property (me, "reg", 0, ®)) + { + unsigned_word attach_address; + int attach_space; + unsigned int attach_size; + + hw_unit_address_to_attach_address (hw_parent (me), + ®.address, + &attach_space, + &attach_address, + me); + rv->reg_address = attach_address; + hw_unit_size_to_attach_size (hw_parent (me), + ®.size, + &attach_size, me); + rv->reg_size = attach_size; + if ((attach_address & 3) != 0) + hw_abort (me, "register block must be 4 byte aligned"); + hw_attach_address (hw_parent (me), + 0, + attach_space, attach_address, attach_size, + me); + } + else + hw_abort (me, "property \"reg\" has the wrong type"); + } + + dummy_prop = hw_find_property (me, "dummy"); + if (dummy_prop != NULL) + { + if (rv->reg_size == 0) + hw_abort (me, "dummy argument requires a \"reg\" property"); + + if (hw_property_type (dummy_prop) == integer_property) + { + unsigned32 dummyfill = hw_find_integer_property (me, "dummy"); + unsigned8 *dummymem = hw_malloc (me, rv->reg_size); + memset (dummymem, dummyfill, rv->reg_size); + rv->dummy = dummymem; + } + else + { + const char *dummyarg = hw_find_string_property (me, "dummy"); + unsigned8 *dummymem = hw_malloc (me, rv->reg_size); + FILE *f = fopen (dummyarg, "rb"); + + if (f == NULL) + hw_abort (me, "opening dummy-file \"%s\": %s", + dummyarg, strerror (errno)); + if (fread (dummymem, 1, rv->reg_size, f) != rv->reg_size) + hw_abort (me, "reading dummy-file \"%s\": %s", + dummyarg, strerror (errno)); + fclose (f); + rv->dummy = dummymem; + } + } + + mbox_prop = hw_find_property (me, "mbox"); + if (mbox_prop != NULL) + { + if (hw_property_type (mbox_prop) == integer_property) + { + signed_cell attach_address_sc + = hw_find_integer_property (me, "mbox"); + + rv->mbox_address = (unsigned32) attach_address_sc; + hw_attach_address (hw_parent (me), + 0, + 0, (unsigned32) attach_address_sc, 4, me); + } + else + hw_abort (me, "property \"mbox\" has the wrong type"); + } + + mem_prop = hw_find_property (me, "mem"); + if (mem_prop != NULL) + { + signed_cell attach_address_sc; + signed_cell attach_size_sc; + + /* Only specific names are reg_array_properties, the rest are + array_properties. */ + if (hw_property_type (mem_prop) == array_property + && hw_property_sizeof_array (mem_prop) == 2 * sizeof (attach_address_sc) + && hw_find_integer_array_property (me, "mem", 0, &attach_address_sc) + && hw_find_integer_array_property (me, "mem", 1, &attach_size_sc)) + { + /* Unfortunate choice of types forces us to dance around a bit. */ + rv->mem_address = (unsigned32) attach_address_sc; + rv->mem_size = (unsigned32) attach_size_sc; + if ((attach_address_sc & 3) != 0) + hw_abort (me, "memory block must be 4 byte aligned"); + } + else + hw_abort (me, "property \"mem\" has the wrong type"); + } + + hw_rv_map_ints (me); + + hw_rv_init_socket (me); + + /* We need an extra initialization pass, after all others currently + scheduled (mostly, after the simulation events machinery has been + initialized so the events we want don't get thrown out). */ + hw_rv_add_init (me); +} + +/* Our root structure; see dv-* build machinery for usage. */ + +const struct hw_descriptor dv_rv_descriptor[] = { + { RV_FAMILY_NAME, hw_rv_finish }, + { NULL } +}; diff --git a/sim/cris/rvdummy.c b/sim/cris/rvdummy.c new file mode 100644 index 00000000000..633eddbfc53 --- /dev/null +++ b/sim/cris/rvdummy.c @@ -0,0 +1,536 @@ +/* Test-driver for the remote-virtual-component simulator framework + for GDB, the GNU Debugger. + + Copyright 2006 Free Software Foundation, Inc. + + This file is part of GDB. + + 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. */ + +/* Avoid any problems whatsoever building this program if we're not + also building hardware support. */ + +#if !WITH_HW +int +main (int argc, char *argv[]) +{ + return 2; +} +#else + +#ifdef HAVE_CONFIG_H +#include "cconfig.h" +#include "tconfig.h" +#endif + +#include "getopt.h" +#include "libiberty.h" + +#define _GNU_SOURCE +#include <stdio.h> + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif + +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif + +#ifdef HAVE_SYS_TIME_H +#include <sys/time.h> +#endif + +#ifdef HAVE_SYS_SELECT_H +#include <sys/select.h> +#endif + +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif + +/* Not guarded in dv-sockser.c, so why here. */ +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <sys/socket.h> + +enum rv_command { + RV_READ_CMD = 0, + RV_WRITE_CMD = 1, + RV_IRQ_CMD = 2, + RV_MEM_RD_CMD = 3, + RV_MEM_WR_CMD = 4, + RV_MBOX_HANDLE_CMD = 5, + RV_MBOX_PUT_CMD = 6, + RV_WATCHDOG_CMD = 7 +}; + +enum opts { OPT_PORT = 1, OPT_TIMEOUT, OPT_VERBOSE }; + +struct option longopts[] = + { + {"port", required_argument, NULL, OPT_PORT}, + {"timeout", required_argument, NULL, OPT_TIMEOUT}, + {"verbose", no_argument, NULL, OPT_VERBOSE}, + {NULL, 0, NULL, 0} + }; + +int port = 10000; +time_t timeout = 30000; +char *progname = "(unknown)"; +int verbose = 0; + +/* Required forward-declarations. */ +static void handle_input_file (int, char *); + +/* Set up a "server" listening to the port in PORT for a raw TCP + connection. Return a file descriptor for the connection or -1 on + error. */ + +int setupsocket (void) +{ + int s; + socklen_t len; + int reuse = 1; + struct sockaddr_in sa_in; + struct sockaddr_in from; + + len = sizeof (from); + memset (&from, 0, len); + memset (&sa_in, 0, sizeof (sa_in)); + + s = socket (AF_INET, SOCK_STREAM, 0); + if (s < 0) + return -1; + + if (setsockopt (s, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof reuse) != 0) + return -1; + + sa_in.sin_port = htons (port); + sa_in.sin_family = AF_INET; + + if (bind (s, (struct sockaddr *) & sa_in, sizeof sa_in) < 0) + return -1; + + if (listen (s, 1) < 0) + return -1; + + return accept (s, (struct sockaddr *) &from, &len); +} + +/* Basic host-to-little-endian 32-bit value. Could use the BFD + machinery, but let's avoid it for this only dependency. */ + +static void +h2le32 (unsigned char *dest, unsigned int val) +{ + dest[0] = val & 255; + dest[1] = (val >> 8) & 255; + dest[2] = (val >> 16) & 255; + dest[3] = (val >> 24) & 255; +} + +/* Send a blob of data. */ + +static void +send_output (int fd, unsigned char *buf, int nbytes) +{ + while (nbytes > 0) + { + ssize_t written = write (fd, buf, nbytes); + if (written < 0) + { + fprintf (stderr, "%s: write to socket failed: %s\n", + progname, strerror (errno)); + exit (2); + } + nbytes -= written; + } +} + +/* Receive a blob of data, NBYTES large. Compare to the first NCOMP + bytes of BUF; if not a match, write error message to stderr and + exit (2). Else put it in buf. */ + +static void +expect_input (int fd, unsigned char *buf, int nbytes, int ncomp) +{ + unsigned char byt; + int i; + + for (i = 0; i < nbytes; i++) + { + int r; + + do + { + errno = 0; + r = read (fd, &byt, 1); + } + while (r <= 0 && (r == 0 || errno == EAGAIN)); + + if (r != 1) + { + fprintf (stderr, "%s: read from socket failed: %s", + progname, strerror (errno)); + exit (2); + } + + if (i < ncomp && byt != buf[i]) + { + int j; + fprintf (stderr, "%s: unexpected input,\n ", progname); + if (i == 0) + fprintf (stderr, "nothing,"); + else + for (j = 0; j < i; j++) + fprintf (stderr, "%02x", buf[j]); + fprintf (stderr, "\nthen %02x instead of %02x\n", byt, buf[i]); + exit (2); + } + else + buf[i] = byt; + } +} + +/* Handle everything about a nil-terminated line of input. + Call exit (2) on error with error text on stderr. */ + +static void +handle_input (int fd, char *buf, char *fname, int lineno) +{ + int nbytes = 0; + int n = -1; + char *s = buf + 2; + unsigned int data; + static unsigned char bytes[1024]; + int i; + + memset (bytes, 0, sizeof bytes); + lineno++; + + if (buf[1] != ',') + goto syntax_error; + + switch (buf[0]) + { + /* Comment characters and empty lines. */ + case 0: case '!': case '#': + break; + + /* Include another file. */ + case '@': + handle_input_file (fd, s); + break; + + /* Raw input (to be expected). */ + case 'i': + do + { + n = -1; + sscanf (s, "%02x%n", &data, &n); + s += n; + if (n > 0) + bytes[nbytes++] = data; + } + while (n > 0); + expect_input (fd, bytes, nbytes, nbytes); + if (verbose) + { + printf ("i,"); + for (i = 0; i < nbytes; i++) + printf ("%02x", bytes[i]); + printf ("\n"); + } + break; + + /* Raw output (to be written). */ + case 'o': + do + { + n = -1; + sscanf (s, "%02x%n", &data, &n); + if (n > 0) + { + s += n; + bytes[nbytes++] = data; + } + } + while (n > 0); + if (*s != 0) + goto syntax_error; + send_output (fd, bytes, nbytes); + if (verbose) + { + printf ("o,"); + for (i = 0; i < nbytes; i++) + printf ("%02x", bytes[i]); + printf ("\n"); + } + break; + + /* Read a register. */ + case 'r': + { + unsigned int addr; + sscanf (s, "%x,%x%n", &addr, &data, &n); + if (n < 0 || s[n] != 0) + goto syntax_error; + bytes[0] = 11; + bytes[1] = 0; + bytes[2] = RV_READ_CMD; + h2le32 (bytes + 3, addr); + expect_input (fd, bytes, 11, 7); + h2le32 (bytes + 7, data); + send_output (fd, bytes, 11); + if (verbose) + printf ("r,%x,%x\n", addr, data); + } + break; + + /* Write a register. */ + case 'w': + { + unsigned int addr; + sscanf (s, "%x,%x%n", &addr, &data, &n); + if (n < 0 || s[n] != 0) + goto syntax_error; + bytes[0] = 11; + bytes[1] = 0; + bytes[2] = RV_WRITE_CMD; + h2le32 (bytes + 3, addr); + h2le32 (bytes + 7, data); + expect_input (fd, bytes, 11, 11); + send_output (fd, bytes, 11); + if (verbose) + printf ("w,%x,%x\n", addr, data); + } + break; + + /* Wait for some milliseconds. */ + case 't': + { + int del = 0; + struct timeval to; + sscanf (s, "%d%n", &del, &n); + if (n < 0 || s[n] != 0 || del == 0) + goto syntax_error; + + to.tv_sec = del / 1000; + to.tv_usec = (del % 1000) * 1000; + + if (select (0, NULL, NULL, NULL, &to) != 0) + { + fprintf (stderr, "%s: problem waiting for %d ms:\n %s\n", + progname, del, strerror (errno)); + exit (2); + } + if (verbose) + printf ("t,%d\n", del); + } + break; + + /* Expect a watchdog command. */ + case 'W': + if (*s != 0) + goto syntax_error; + bytes[0] = 3; + bytes[1] = 0; + bytes[2] = RV_WATCHDOG_CMD; + expect_input (fd, bytes, 3, 3); + if (verbose) + printf ("W\n"); + break; + + /* Send an IRQ notification. */ + case 'I': + sscanf (s, "%x%n", &data, &n); + if (n < 0 || s[n] != 0) + goto syntax_error; + bytes[0] = 7; + bytes[1] = 0; + bytes[2] = RV_IRQ_CMD; + h2le32 (bytes + 3, data); + send_output (fd, bytes, 7); + if (verbose) + printf ("I,%x\n", data); + break; + + /* DMA store (to CPU). */ + case 's': + { + unsigned int addr; + sscanf (s, "%x,%n", &addr, &n); + + if (n < 0 || s[n] == 0) + goto syntax_error; + s += n; + do + { + n = -1; + sscanf (s, "%02x%n", &data, &n); + if (n > 0) + { + s += n; + bytes[11 + nbytes++] = data; + } + } + while (n > 0); + + if (*s != 0) + goto syntax_error; + h2le32 (bytes, nbytes + 11); + bytes[2] = RV_MEM_WR_CMD; + h2le32 (bytes + 3, addr); + h2le32 (bytes + 7, nbytes); + send_output (fd, bytes, nbytes + 11); + if (verbose) + { + printf ("s,%x,", addr); + for (i = 0; i < nbytes; i++) + printf ("%02x", bytes[i]); + printf ("\n"); + } + } + break; + + /* DMA load (from CPU). */ + case 'l': + { + unsigned int addr; + sscanf (s, "%x,%n", &addr, &n); + + if (n < 0 || s[n] == 0) + goto syntax_error; + s += n; + do + { + n = -1; + sscanf (s, "%02x%n", &data, &n); + if (n > 0) + { + s += n; + bytes[11 + nbytes++] = data; + } + } + while (n > 0); + + if (*s != 0) + goto syntax_error; + h2le32 (bytes, nbytes + 11); + bytes[0] = 11; + bytes[1] = 0; + bytes[2] = RV_MEM_RD_CMD; + h2le32 (bytes + 3, addr); + h2le32 (bytes + 7, nbytes); + send_output (fd, bytes, 11); + bytes[0] = (nbytes + 11) & 255; + bytes[1] = ((nbytes + 11) >> 8) & 255; + expect_input (fd, bytes, nbytes + 11, nbytes + 11); + if (verbose) + { + printf ("l,%x,", addr); + for (i = 0; i < nbytes; i++) + printf ("%02x", bytes[i]); + printf ("\n"); + } + } + break; + + syntax_error: + default: + fprintf (stderr, "%s: invalid command line in %s:%d:\n %s", + progname, fname, lineno, strerror (errno)); + exit (2); + } +} + +/* Loop over the contents of FNAME, using handle_input to parse each line. + Errors to stderr, exit (2). */ + +static void +handle_input_file (int fd, char *fname) +{ + static char buf[2048] = {0}; + int lineno = 0; + FILE *f = fopen (fname, "r"); + + if (f == NULL) + { + fprintf (stderr, "%s: problem opening %s: %s\n", + progname, fname, strerror (errno)); + exit (2); + } + + /* Let's cut the buffer short, so we always get a newline. */ + while (fgets (buf, sizeof (buf) - 1, f) != NULL) + { + buf[strlen (buf) - 1] = 0; + lineno++; + handle_input (fd, buf, fname, lineno); + } + + fclose (f); +} + +int +main (int argc, char *argv[]) +{ + int optc; + int fd; + FILE *f; + int i; + + progname = argv[0]; + while ((optc = getopt_long (argc, argv, "", longopts, NULL)) != -1) + switch (optc) + { + case OPT_PORT: + port = atoi (optarg); + break; + + case OPT_TIMEOUT: + timeout = (time_t) atoi (optarg); + break; + + case OPT_VERBOSE: + verbose = 1; + break; + } + + fd = setupsocket (); + if (fd < 0) + { + fprintf (stderr, "%s: problem setting up the connection: %s\n", + progname, strerror (errno)); + exit (2); + } + + for (i = optind; i < argc; i++) + handle_input_file (fd, argv[i]); + + /* FIXME: option-controlled test for remaining input? */ + close (fd); + return 1; +} +#endif diff --git a/sim/cris/sim-if.c b/sim/cris/sim-if.c index 507ba579464..86d19b2f187 100644 --- a/sim/cris/sim-if.c +++ b/sim/cris/sim-if.c @@ -1,5 +1,5 @@ /* Main simulator entry points specific to the CRIS. - Copyright (C) 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc. Contributed by Axis Communications. This file is part of the GNU simulators. @@ -78,7 +78,7 @@ static CGEN_DISASSEMBLER cris_disassemble_insn; static char cris_bare_iron = 0; /* Whether 0x9000000xx have simulator-specific meanings. */ -static char cris_have_900000xxif = 0; +char cris_have_900000xxif = 0; /* Records simulator descriptor so utilities like cris_dump_regs can be called from gdb. */ @@ -506,7 +506,15 @@ sim_open (SIM_OPEN_KIND kind, host_callback *callback, struct bfd *abfd, memset (cpu->sighandler, 0, sizeof (cpu->sighandler)); cpu->make_thread_cpu_data = NULL; cpu->thread_cpu_data_size = 0; +#if WITH_HW + cpu->deliver_interrupt = NULL; +#endif } +#if WITH_HW + /* Always be cycle-accurate and call before/after functions if + with-hardware. */ + sim_profile_set_option (sd, "-model", PROFILE_MODEL_IDX, "on"); +#endif } /* Initialize various cgen things not done by common framework. diff --git a/sim/cris/sim-main.h b/sim/cris/sim-main.h index b35b927278e..483046d90ee 100644 --- a/sim/cris/sim-main.h +++ b/sim/cris/sim-main.h @@ -1,5 +1,5 @@ /* Main header for the CRIS simulator, based on the m32r header. - Copyright (C) 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc. Contributed by Axis Communications. This file is part of the GNU simulators. @@ -117,6 +117,10 @@ struct cris_thread_info { char sigsuspended; }; +typedef int (*cris_interrupt_delivery_fn) (SIM_CPU *, + enum cris_interrupt_type, + unsigned int); + struct _sim_cpu { /* sim/common cpu base. */ sim_cpu_base base; @@ -132,6 +136,11 @@ struct _sim_cpu { CRIS_MISC_PROFILE cris_prev_misc_profile; #define CPU_CRIS_PREV_MISC_PROFILE(cpu) (& (cpu)->cris_prev_misc_profile) +#if WITH_HW + cris_interrupt_delivery_fn deliver_interrupt; +#define CPU_CRIS_DELIVER_INTERRUPT(cpu) (cpu->deliver_interrupt) +#endif + /* Simulator environment data. */ USI endmem; USI endbrk; |