[patch] Reverse Execution in SID, Reverse Debugging with GDB and SID

classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|

[patch] Reverse Execution in SID, Reverse Debugging with GDB and SID

Dave Brolley-2
Hi,

The attached patches implement

1) Infrastructure for reverse execution of in SID
2) Target specific implementation for xstormy16

This work is intended to be used in conjunction with Michael Snyder's
work on reverse debugging in GDB, but I suppose that the idea of
executing in reverse need not necessarily be restricted only to this
purpose.

I'm submitting the general infrastructure work for approval to commit.
The xstormy16 specific implementation was done mainly as an example, and
perhaps should also be committed.

Here is an overview of the changes and how they work:

o The changes the the scheduling components are mainly to have them
drive the value of 'now' for each scheduled event. Currently they drive
the value zero which is not used on the receiving end. This provides a
mechanism for a scheduled component to know what 'time' each scheduled
event occurs. Components, like memory, which are not scheduled, but
which need to know at what 'time' an event occurred can query the
scheduler's time attribute to obtain the same information.  The
scheduler's set_time method has also been updated so that it cancels all
pending events. This change was probably already necessary, but the need
was exposed during testing of this feature.

o A new component class mix-in called reversible_component has been
created to consolidate the common needs of a component which may have to
step back in 'time'. The main features are a reversible? attribute which
tells the component that it should be keeping track of events and a
restore-to-time! pin which tells the component to restore it's state to
a given 'time'.

o A new utility class called change_log has been added to aid those
components which choose to implement this using change logging techniques.

o The basic_cpu class has been given specific knowledge of what it means
to execute in reverse. This is controlled by it's new exec-direction
attribute which can be set to "forward" or "backward". This attribute is
checked when the step-insns pin is driven. If the direction is forward,
then it's business as usual. If it's backward, then the cpu does what is
necessary to step backward and then resets the scheduler to that 'time'.
The scheduler in turn drives its time-set pin to notify other components
in the system to restore themselves to that 'time'. Note that reverse
execution need not be one insn at a time. I did this for the xstormy16
example so that stepi would work in reverse with gdb. Note that in the
xstormy16 example, breakpoints and watchpoints are also supported while
executing in reverse.

The idea is that components in the system which have state implement
some method of restoring their state to what it was at a given 'time'.
Whatever makes the most sense for each particular component. The cpu is
an obvious example as is memory. More complex systems may have other
components with this requirement. For the xstormy16, cpu and memory are
the only components requiring this capability.

In particular, the xstormy16 cpu component needed only to track changes
in the pc and the gr registers. Using specific knowlege of the kinds of
changes that are possible to the pc, in particular I was able to
implement a change logging system that uses only 1 byte for small pc
changes (i.e. less than 128 bytes) and 3 bytes otherwise. Similarly for
the gr registers, a 2 byte mask indicates which registers have changed
and then only the changed registers are added to the change log record.
Many change log records are therefore 5 bytes or less.

One interesting 'feature' of the current implementation is that if a
program has been debugged to completion and then debugging has started
again (i.e. the gdb 'target' command establishes a new connection with
SID), one can debug backward past the beginning of the program (with a
warning from SID) and back into the previous execution instance. The
feature is handy in the case that you use the GDB continue command and
end up at the end of the program by mistake.

Comments, ideas, and, of course, approval to commit the infrastructure
patch please. I can also commit the xstormy16 specific parts if desired.

Dave


sid/main/dynamic/ChangeLog:
2006-09-15  Dave Brolley  <[hidden email]>

        * mainDynamic.cxx (usage): Document --reversible.
        (try_add_memory): Call sess->add_memory.
        (option_num): add opt_reversible.
        (long_options): Add "reversible".
        (main): Handle opt_reversible.
        * commonCfg.h (set_reversible): New method of SessionCfg.
        (add_memory): Likewise.
        (reversible_p): New member of SessionCfg.
        (memory): Likewise.
        * commonCfg.cxx (CpuCfg): Establish sim-sched relation.
        (SessionCfg): Initialize reversible_p.
        (SessionCfg::write_load): Set up all memory regions as reversible,
        if requested.
        (BoardCfg::write_config): Set up the cpu to be reversible, if
        requested.

sid/include/ChangeLog:
2006-09-15  Dave Brolley  <[hidden email]>

        * sidmiscutil.h (change_log): New utility class.
        * sidcpuutil.h (basic_cpu): Now inherits from reversible_component.
        (step_pin_handler): Parameter now named 'tick'. Save the current
        tick. Handle exec_direction == "backward". Initialize change logging,
        if reversible. Finish change logging, if reversible.
        (step_backward): New method of basic_cpu.
        (reset_pin_handler): Terminte the current change log, if any.
        Set exec_direction to "forward".
        (change_log,change_log_begin,change_log_end,change_log_boundaries)
        (change_string,exec_direction,sim_sched,current_tick,last_tick): New
        members of basic_cpu.
        (init_change_logging,finish_change_logging,log_change)
        (restore_state_to_time): New virtual methods of basic_cpu.
        (basic_cpu): Initialize change_log, change_log_begin,
        change_log_end, change_log_boundaries, last_tick, exec_direction,
        and sim_sched. Add the exec-direction and sim-sched attributes.
        * sidattrutil.h (reversible_component): New mix-in class.

sid/component/sched/ChangeLog:
2006-09-15  Dave Brolley  <[hidden email]>

        * compSched.cxx (deliver_regular): Drive rnext->when.
        (deliver_irregular): Drive irnext->when.
        (advancy_any): Check yield_step_loop_p.
        (cancel_all): New method of generic_scheduler.
        (time_set_pin): New member of scheduler_component.
        (scheduler_component::set_time): Call set_now with 'then - 1'.
        Call sched.cancel_all. Drive time_set_pin.
        (scheduler_component_ctor_1): Add time-set pin.

sid/component/memory/ChangeLog:
2006-09-15  Dave Brolley  <[hidden email]>

        * generic.h: Remove 'using sidutil::no_relation_component'. Add
        'using sidutil::fixed_relation_map_component'.
        (generic_memory): New inherits from reversible_component and
        fixed_relation_map_component. Now inherits virtually from
        fixed_pin_map_component and fixed_attribute_map_component.
        (sched): New member of generic_memory.
        (change_log): Likewise.
        (record_update): New method of generic_memory.
        (restore_state_to_time): Likewise.
        (generic_read_write_bus::write_any): call record_update if this
        memory is reversible.
        * generic.cxx (generic_memory): Initialize sched and
        change_log. Add sim-sched relation.
        (record_update): New method of generic_memory.
        (restore_state_to_time): Likewise.

sid/component/gdb/ChangeLog:
2006-09-15  Dave Brolley  <[hidden email]>

        * gdbserv-target.h (set_exec_direction): New member of
        gdbserv_target.
        * gdbserv-state.c (gdbserv_data_packet): Initialize exec_direction.
        Check for a 'b' prefix for the S, s, C, and c packets.  Call
        gdbserv->target->set_exec_direction.
        * gdb.h (set_exec_direction): New function prototype.
        * gdb.cxx (set_exec_direction_hook): New function.
        (gdbsid_target_attach): Set gdbtarget->set_exec_direction.
        (set_exec_direction): New function.



cgen/cpu/ChangeLog:
2006-09-15  Dave Brolley  <[hidden email]>

        * cpu/xstormy16.cpu (h-pc): Add a set handler.
        (h-gr): Likewise.

sid/component/cgen-cpu/xstormy16/ChangeLog:
2006-09-15  Dave Brolley  <[hidden email]>

        * xstormy16.h (xstormy16_cpu_cgen): Now inherits from
        cgen_bi_endian_cpu.
        (h_pc_set_handler): New method of xstormy16_cpu_cgen.
        (h_gr_set_handler,init_change_logging,finish_change_logging)
        (log_pc_change,log_gr_change,restore_change) Likewise.
        (PC_UNCHANGED,PC_RESET,gr_changed,pc_changed,old_gr,old_h_pc):
        New members of xstormy16_cpu_cgen.
        (xstormy16_cpu): Now inherits only from xstormy16_cpu_cgen.
        * xstormy16.cxx (init_change_logging): New method of
        xstormy16_cpu_cgen.
        (finish_change_logging,log_pc_change,log_gr_change,restore_change):
        Likewise.
        * xstormy16-cpu.h: Regenerated.



Index: sid/component/gdb/gdb.cxx
===================================================================
RCS file: /cvs/src/src/sid/component/gdb/gdb.cxx,v
retrieving revision 1.17
diff -c -p -r1.17 gdb.cxx
*** sid/component/gdb/gdb.cxx 1 Mar 2006 21:07:00 -0000 1.17
--- sid/component/gdb/gdb.cxx 15 Sep 2006 20:27:52 -0000
***************
*** 1,6 ****
  // gdb.cxx - GDB stub implementation.  -*- C++ -*-
 
! // Copyright (C) 1999-2002, 2004, 2005 Red Hat.
  // This file is part of SID and is licensed under the GPL.
  // See the file COPYING.SID for conditions for redistribution.
 
--- 1,6 ----
  // gdb.cxx - GDB stub implementation.  -*- C++ -*-
 
! // Copyright (C) 1999-2002, 2004, 2005, 2006 Red Hat.
  // This file is part of SID and is licensed under the GPL.
  // See the file COPYING.SID for conditions for redistribution.
 
*************** process_detach_hook (struct gdbserv *gdb
*** 232,237 ****
--- 232,244 ----
    return g->process_detach ();
  }
 
+ extern "C" int
+ set_exec_direction_hook (struct gdbserv *gdbserv, const char *direction)
+ {
+   gdb* g = static_cast<gdb*> (gdbserv_target_data (gdbserv));
+   return g->set_exec_direction (direction);
+ }
+
 
 
 
*************** gdb::gdbsid_target_attach (struct gdbser
*** 278,283 ****
--- 285,291 ----
        gdbtarget->rangestep_program = rangestep_program_hook;
        gdbtarget->sigkill_program = sigkill_program_hook;
        gdbtarget->continue_program = continue_program_hook;
+       gdbtarget->set_exec_direction = set_exec_direction_hook;
        gdbtarget->remove_breakpoint = remove_breakpoint_hook;
        gdbtarget->set_breakpoint = set_breakpoint_hook;
        gdbtarget->detach = process_detach_hook;
*************** gdb::add_sw_breakpoint (host_int_8 addre
*** 1329,1334 ****
--- 1337,1359 ----
  }
 
 
+ int
+ gdb::set_exec_direction (const char *direction)
+ {
+   if (trace_gdbsid)
+     cerr << "set_exec_direction " << endl;
+
+   assert (cpu != 0);
+   component::status s = cpu->set_attribute_value ("exec-direction", direction);
+   if (s != component::ok)
+     {
+       cerr << "Cannot set exec-direction attribute in cpu: status " << (int)s << endl;
+     }
+
+   return 0;
+ }
+
+
  bool
  gdb::add_hw_watchpoint (host_int_8 address, host_int_4 length)
  {
Index: sid/component/gdb/gdb.h
===================================================================
RCS file: /cvs/src/src/sid/component/gdb/gdb.h,v
retrieving revision 1.11
diff -c -p -r1.11 gdb.h
*** sid/component/gdb/gdb.h 14 Nov 2005 20:04:53 -0000 1.11
--- sid/component/gdb/gdb.h 15 Sep 2006 20:27:52 -0000
***************
*** 1,6 ****
  // gdb.h - description.  -*- C++ -*-
 
! // Copyright (C) 1999, 2000, 2001, 2002, 2005 Red Hat.
  // This file is part of SID and is licensed under the GPL.
  // See the file COPYING.SID for conditions for redistribution.
 
--- 1,6 ----
  // gdb.h - description.  -*- C++ -*-
 
! // Copyright (C) 1999, 2000, 2001, 2002, 2005, 2006 Red Hat.
  // This file is part of SID and is licensed under the GPL.
  // See the file COPYING.SID for conditions for redistribution.
 
*************** public:
*** 216,221 ****
--- 216,222 ----
    int Z_breakpoint_ok_p (unsigned long type, struct gdbserv_reg *addr, struct gdbserv_reg *len);
    int remove_breakpoint (unsigned long type, struct gdbserv_reg *addr, struct gdbserv_reg *len);
    int set_breakpoint (unsigned long type, struct gdbserv_reg *addr, struct gdbserv_reg *len);
+   int set_exec_direction (const char* direction);
    void process_detach ();
  };
 
Index: sid/component/gdb/gdbserv-state.c
===================================================================
RCS file: /cvs/src/src/sid/component/gdb/gdbserv-state.c,v
retrieving revision 1.3
diff -c -p -r1.3 gdbserv-state.c
*** sid/component/gdb/gdbserv-state.c 12 Feb 2005 16:25:46 -0000 1.3
--- sid/component/gdb/gdbserv-state.c 15 Sep 2006 20:27:52 -0000
***************
*** 1,7 ****
  /*
   * gdbserv-state.c -- part of GDB remote server
   *
!  * Copyright (C) 2000, 2002 Red Hat.
   * This file is part of SID and is licensed under the GPL.
   * See the file COPYING.SID for conditions for redistribution.
   */
--- 1,7 ----
  /*
   * gdbserv-state.c -- part of GDB remote server
   *
!  * Copyright (C) 2000, 2002, 2006 Red Hat.
   * This file is part of SID and is licensed under the GPL.
   * See the file COPYING.SID for conditions for redistribution.
   */
*************** gdbserv_fromclient_data (struct gdbserv
*** 123,134 ****
--- 123,151 ----
  void
  gdbserv_data_packet (struct gdbserv *gdbserv)
  {
+   const char *exec_direction = "forward";
    char packet_type = gdbserv_input_char (gdbserv);
    if (gdbserv_state_trace)
      fprintf (gdbserv_state_trace, "<gdbserv_data_packet:%c>\n", packet_type);
   
    /* NB: default is for this to send an empty packet */
 
+   /* Check for a 'b' (backward) prefix for S, s, C and c.  This indicates that
+      the direction of execution is to be backward.  */
+   if (packet_type == 'b')
+     {
+       char next = gdbserv_input_peek (gdbserv);
+       switch (next)
+ {
+ case 'S': case 's': case 'C': case 'c':
+  exec_direction = "backward";
+  packet_type = gdbserv_input_char (gdbserv);
+  break;
+ default:
+  break;
+ }
+     }
+
    switch (packet_type)
      {
 
*************** gdbserv_data_packet (struct gdbserv *gdb
*** 461,467 ****
     gdbserv->target->sigkill_program (gdbserv);
     return;
   }
!
  /* Set machine state to force a single step.  */
  if (packet_type == 's' || packet_type == 'S')
   {
--- 478,487 ----
     gdbserv->target->sigkill_program (gdbserv);
     return;
   }
!
! /* Set the direction.  */
! gdbserv->target->set_exec_direction (gdbserv, exec_direction);
!
  /* Set machine state to force a single step.  */
  if (packet_type == 's' || packet_type == 'S')
   {
Index: sid/component/gdb/gdbserv-target.h
===================================================================
RCS file: /cvs/src/src/sid/component/gdb/gdbserv-target.h,v
retrieving revision 1.2
diff -c -p -r1.2 gdbserv-target.h
*** sid/component/gdb/gdbserv-target.h 12 Feb 2002 21:58:58 -0000 1.2
--- sid/component/gdb/gdbserv-target.h 15 Sep 2006 20:27:52 -0000
***************
*** 1,7 ****
  /*
   * gdbserv-target.h -- part of GDB remote server.
   *
!  * Copyright (C) 2000, 2002 Red Hat.
   * This file is part of SID and is licensed under the GPL.
   * See the file COPYING.SID for conditions for redistribution.
   */
--- 1,7 ----
  /*
   * gdbserv-target.h -- part of GDB remote server.
   *
!  * Copyright (C) 2000, 2002, 2006 Red Hat.
   * This file is part of SID and is licensed under the GPL.
   * See the file COPYING.SID for conditions for redistribution.
   */
*************** struct gdbserv_target {
*** 125,130 ****
--- 125,131 ----
    void (*cyclestep_program) (struct gdbserv *);
    void (*sigkill_program) (struct gdbserv *);
    int (*continue_program) (struct gdbserv *);
+   int (*set_exec_direction) (struct gdbserv *, const char *direction);
  #endif
 
    /* Breakpoint methods */
Index: sid/component/memory/generic.cxx
===================================================================
RCS file: /cvs/src/src/sid/component/memory/generic.cxx,v
retrieving revision 1.8
diff -c -p -r1.8 generic.cxx
*** sid/component/memory/generic.cxx 10 Jun 2003 18:27:10 -0000 1.8
--- sid/component/memory/generic.cxx 15 Sep 2006 20:27:52 -0000
***************
*** 1,6 ****
  // generic.cxx - a class of generic memories.  -*- C++ -*-
 
! // Copyright (C) 1999-2001,2003 Red Hat.
  // This file is part of SID and is licensed under the GPL.
  // See the file COPYING.SID for conditions for redistribution.
 
--- 1,6 ----
  // generic.cxx - a class of generic memories.  -*- C++ -*-
 
! // Copyright (C) 1999-2001, 2003, 2006 Red Hat.
  // This file is part of SID and is licensed under the GPL.
  // See the file COPYING.SID for conditions for redistribution.
 
*************** generic_memory::generic_memory() throw (
*** 54,59 ****
--- 54,61 ----
    imagestore_pin (this, & generic_memory::imagestore_handler),
    imagemmap_pin (this, & generic_memory::imagemmap_handler),
    imagemsync_pin (this, & generic_memory::imagemsync_handler),
+   sched (0),
+   change_log (0x60000),
    read_latency (0),
    write_latency (0)
  {
*************** generic_memory::generic_memory() throw (
*** 86,91 ****
--- 88,95 ----
    add_attribute_virtual ("state-snapshot", this,
  & generic_memory::save_state,
  & generic_memory::restore_state);
+
+   add_uni_relation ("sim-sched", & sched);
  }
 
 
*************** generic_memory::imagemmap_handler (host_
*** 263,271 ****
--- 267,341 ----
  }
 
 
+ // Record a change in this memory region, so that it may be restored later.
+ void
+ generic_memory::record_update (host_int_4 address, const void *bytes, unsigned width)
+ {
+   // This function is only used during reverse debugging at the moment.
+   assert (reversible_p);
+
+   // Make sure we can get the current time
+   if (! sched)
+     return;
+   string tick_attr = sched->attribute_value ("time");
+   host_int_4 tick;
+   component::status s = parse_attribute (tick_attr, tick);
+   if (s != component::ok)
+     return;
+
+   // Make sure that the time is not 0.
+   if (tick == 0)
+     return;
+
+   // Assemble the change log entry
+   char change[4 + 8]; // max buffer size
+   *(sid::host_int_4 *)change = address;
+   memcpy (change + 4, this->buffer + address, width);
+
+   // The change log record contains the current tick, the address and the
+   // original data.
+   change_log.push (& tick, sizeof (tick));
+   change_log.add (change, 4 + width);
+   change_log.finish ();
+ }
 
 
+ // Restore this mrmory region to the state it was at the given time (tick).
+ void
+ generic_memory::restore_state_to_time (sid::host_int_4 tick)
+ {
+   // Call up to the base class.
+   reversible_component::restore_state_to_time (tick);
 
+   // Undo all updates back to the given time.
+   while (! change_log.empty ())
+     {
+       sid::host_int_4 length;
+       const unsigned char *record = (const unsigned char *)change_log.top (length);
+
+       // The time (tick) of the change is the first item in the record.
+       // If this record occurred previous to our target time, then we are done.
+       sid::host_int_4 new_tick = *(sid::host_int_4*)record;
+       if (new_tick < tick)
+ break;
+
+       record += sizeof (new_tick);
+       length -= sizeof (new_tick);
+
+       // The next item in the record is the address which was changed.
+       sid::host_int_4 address = *(sid::host_int_4*)record;
+       record += sizeof (address);
+       length -= sizeof (address);
+
+       // The remainder of the record contains the original data.
+       // Restore the change.
+       memcpy (this->buffer + address, record, length);
+
+       // Done with this record.
+       change_log.pop ();
+     }
+ }
+
  // ----------------------------------------------------------------------------
 
 
Index: sid/component/memory/generic.h
===================================================================
RCS file: /cvs/src/src/sid/component/memory/generic.h,v
retrieving revision 1.5
diff -c -p -r1.5 generic.h
*** sid/component/memory/generic.h 3 Aug 2001 06:02:46 -0000 1.5
--- sid/component/memory/generic.h 15 Sep 2006 20:27:52 -0000
***************
*** 1,6 ****
  // generic.h - Header for the generic_memory class.  -*- C++ -*-
 
! // Copyright (C) 1999-2001 Red Hat.
  // This file is part of SID and is licensed under the GPL.
  // See the file COPYING.SID for conditions for redistribution.
 
--- 1,6 ----
  // generic.h - Header for the generic_memory class.  -*- C++ -*-
 
! // Copyright (C) 1999-2001, 2006 Red Hat.
  // This file is part of SID and is licensed under the GPL.
  // See the file COPYING.SID for conditions for redistribution.
 
*************** using sid::big_int_8;
*** 50,57 ****
  using sidutil::fixed_pin_map_component;
  using sidutil::no_accessor_component;
  using sidutil::fixed_attribute_map_component;
! using sidutil::no_relation_component;
  using sidutil::fixed_bus_map_component;
  using sidutil::std_error_string;
  using sidutil::callback_pin;
  using sidutil::output_pin;
--- 50,58 ----
  using sidutil::fixed_pin_map_component;
  using sidutil::no_accessor_component;
  using sidutil::fixed_attribute_map_component;
! using sidutil::fixed_relation_map_component;
  using sidutil::fixed_bus_map_component;
+ using sidutil::reversible_component;
  using sidutil::std_error_string;
  using sidutil::callback_pin;
  using sidutil::output_pin;
*************** using sidutil::output_pin;
*** 61,71 ****
 
 
  class generic_memory: public virtual component,
!      protected fixed_pin_map_component,
       protected no_accessor_component,
!      protected fixed_attribute_map_component,
!      protected no_relation_component,
!      protected fixed_bus_map_component
  {
  public:
    generic_memory() throw (bad_alloc);
--- 62,73 ----
 
 
  class generic_memory: public virtual component,
!      protected virtual fixed_pin_map_component,
       protected no_accessor_component,
!      protected virtual fixed_attribute_map_component,
!      protected fixed_relation_map_component,
!      protected fixed_bus_map_component,
!      protected reversible_component
  {
  public:
    generic_memory() throw (bad_alloc);
*************** protected:
*** 94,99 ****
--- 96,108 ----
    host_int_4 max_buffer_length;
    bool attempt_resize (host_int_4 new_length) throw();
 
+ protected:
+   // Change logging for the purpose of reverse simulation.
+   component *sched;
+   sidutil::change_log change_log;
+   void record_update (host_int_4 address, const void *bytes, unsigned width);
+   virtual void restore_state_to_time (sid::host_int_4 tick);
+
  private:
    string get_size_attr ();
    component::status set_size_attr (const string& s);
*************** generic_read_write_bus::write_any(host_i
*** 211,216 ****
--- 220,227 ----
    if (LIKELY((address >= 0) && ((address+width) <= target->buffer_length)))
      {
        typename DataType::value_type mem_image = data.target_memory_value();
+       if (UNLIKELY (target->reversible_p))
+ target->record_update (address, & mem_image, width);
        memcpy (& target->buffer[address], & mem_image, width);
        bus::status st (bus::ok);
        st.latency = target->write_latency;
Index: sid/component/sched/compSched.cxx
===================================================================
RCS file: /cvs/src/src/sid/component/sched/compSched.cxx,v
retrieving revision 1.14
diff -c -p -r1.14 compSched.cxx
*** sid/component/sched/compSched.cxx 1 Mar 2006 21:07:02 -0000 1.14
--- sid/component/sched/compSched.cxx 15 Sep 2006 20:27:52 -0000
***************
*** 1,6 ****
  // compSched.cxx - the scheduler component.  -*- C++ -*-
 
! // Copyright (C) 1999-2003, 2005 Red Hat.
  // This file is part of SID and is licensed under the GPL.
  // See the file COPYING.SID for conditions for redistribution.
  #include "config.h"
--- 1,6 ----
  // compSched.cxx - the scheduler component.  -*- C++ -*-
 
! // Copyright (C) 1999-2003, 2005, 2006 Red Hat.
  // This file is part of SID and is licensed under the GPL.
  // See the file COPYING.SID for conditions for redistribution.
  #include "config.h"
*************** operator >> (istream& i, exact_host_time
*** 639,645 ****
   this->refill_regular_events_table();
  // deliver event
  assert (victim);
! victim->drive (0);
        }
 
 
--- 639,645 ----
   this->refill_regular_events_table();
  // deliver event
  assert (victim);
! victim->drive (rnext->when);
        }
 
 
*************** operator >> (istream& i, exact_host_time
*** 656,662 ****
  this->irregular_events.pop_back();
  // deliver event
  assert (victim);
! victim->drive (0);
        }
 
 
--- 656,662 ----
  this->irregular_events.pop_back();
  // deliver event
  assert (victim);
! victim->drive (irnext->when);
        }
 
 
*************** operator >> (istream& i, exact_host_time
*** 721,727 ****
     evpair = this->next_event ();
   } while ((evpair.first != 0) // still an event
    && (evpair.first->when <= now) // still due
!   && (due_count < due_limit)); // not too many iterations
 
  // cout << "sid-sched: delivered " << due_count << " due/overdue events." << endl;
 
--- 721,728 ----
     evpair = this->next_event ();
   } while ((evpair.first != 0) // still an event
    && (evpair.first->when <= now) // still due
!   && (due_count < due_limit)  // not too many iterations
!   && ! this->yield_step_loop_p);
 
  // cout << "sid-sched: delivered " << due_count << " due/overdue events." << endl;
 
*************** operator >> (istream& i, exact_host_time
*** 871,876 ****
--- 872,886 ----
        }
 
 
+     // Cancel all pending events.
+     void
+     cancel_all ()
+       {
+ this->irregular_events.clear ();
+ this->regular_table_iter = this->regular_events_table.end();
+ this->yield_step_loop_p = true;
+       }
+
      // Add a pin<->string mapping
      void
      clear_pin_mappings ()
*************** class scheduler_component: public schedu
*** 1357,1362 ****
--- 1367,1373 ----
    output_pin time_low_pin;
    output_pin time_high_pin;
    output_pin active_pin;
+   output_pin time_set_pin;
 
  public:
 
*************** private:
*** 1379,1391 ****
        tick_t then;
        component::status s = parse_attribute(t, then);
        if (UNLIKELY(s != component::ok)) return s;
!       this->sched.set_now (then);
        tick_t now;
        this->sched.get_now (now);
!       if (then != now)
  return component::bad_value;
!       else
! return component::ok;
      }
 
  protected:
--- 1390,1403 ----
        tick_t then;
        component::status s = parse_attribute(t, then);
        if (UNLIKELY(s != component::ok)) return s;
!       this->sched.set_now (then - 1);
        tick_t now;
        this->sched.get_now (now);
!       if (then - 1 != now)
  return component::bad_value;
!       this->sched.cancel_all ();
!       time_set_pin.drive (then);
!       return component::ok;
      }
 
  protected:
*************** scheduler_component<Scheduler>::schedule
*** 1486,1491 ****
--- 1498,1504 ----
    add_pin ("time-low", & this->time_low_pin);
    add_pin ("yield", & this->yield_pin);
    add_pin ("active", & this->active_pin);
+   add_pin ("time-set", & this->time_set_pin);
    add_attribute ("yield", & this->yield_pin, "pin");
    add_attribute ("enable-threshold", & this->enable_threshold, "setting");
    add_attribute ("enabled?", & this->enable_p, "setting");
Index: sid/include/sidattrutil.h
===================================================================
RCS file: /cvs/src/src/sid/include/sidattrutil.h,v
retrieving revision 1.9
diff -c -p -r1.9 sidattrutil.h
*** sid/include/sidattrutil.h 27 Mar 2006 20:30:06 -0000 1.9
--- sid/include/sidattrutil.h 15 Sep 2006 20:27:53 -0000
***************
*** 2,8 ****
  // mappings between application objects and their string
  // representations.  -*- C++ -*-
 
! // Copyright (C) 1999, 2000, 2003, 2005 Red Hat.
  // This file is part of SID and is licensed under the GPL.
  // See the file COPYING.SID for conditions for redistribution.
 
--- 2,8 ----
  // mappings between application objects and their string
  // representations.  -*- C++ -*-
 
! // Copyright (C) 1999, 2000, 2003, 2005, 2006 Red Hat.
  // This file is part of SID and is licensed under the GPL.
  // See the file COPYING.SID for conditions for redistribution.
 
*************** protected:
*** 1127,1132 ****
--- 1127,1156 ----
        }
    };
 
+
+   // A mix-in for components which need to save and restore state
+   // at given time indices.
+   class reversible_component :
+     public virtual fixed_pin_map_component,
+     public virtual fixed_attribute_map_component
+   {
+   public:
+     reversible_component () :
+       reversible_p (false),
+       restore_to_time_pin (this, & reversible_component::restore_state_to_time)
+       {
+ add_pin ("restore-to-time!", & this->restore_to_time_pin);
+ add_attribute ("reversible?", & reversible_p, "setting");
+       }
+
+     ~reversible_component () throw() {}
+
+   protected:
+     bool reversible_p;
+
+     virtual void restore_state_to_time (sid::host_int_4) {}
+     callback_pin<reversible_component> restore_to_time_pin;
+   };
  }
 
  #endif // SIDATTRUTIL_H
Index: sid/include/sidcpuutil.h
===================================================================
RCS file: /cvs/src/src/sid/include/sidcpuutil.h,v
retrieving revision 1.38
diff -c -p -r1.38 sidcpuutil.h
*** sid/include/sidcpuutil.h 26 Jun 2006 21:04:00 -0000 1.38
--- sid/include/sidcpuutil.h 15 Sep 2006 20:27:53 -0000
***************
*** 16,21 ****
--- 16,22 ----
  #include <sidschedutil.h>
 
  using std::string;
+ using std::pair;
 
  namespace sidutil
  {
*************** namespace sidutil
*** 106,112 ****
    protected virtual fixed_attribute_map_component,
    protected virtual fixed_relation_map_component,
    protected virtual fixed_bus_map_component,
!   protected virtual configurable_component
    {
      // custom memory allocators for poisioning freshly-allocated memory
    public:
--- 107,114 ----
    protected virtual fixed_attribute_map_component,
    protected virtual fixed_relation_map_component,
    protected virtual fixed_bus_map_component,
!   protected virtual configurable_component,
!   protected virtual reversible_component
    {
      // custom memory allocators for poisioning freshly-allocated memory
    public:
*************** namespace sidutil
*** 308,319 ****
      bool enable_step_trap_p;
      cpu_trace_stream trace_stream;
 
!     virtual void step_pin_handler (sid::host_int_4)
        {
  recursion_record limit (& this->step_limit);
  if (UNLIKELY(! limit.ok())) return;
 
  this->yield_p = false;
 
  // Check for triggerpoints due right now; may set yield_p!
  this->triggerpoint_manager.check_and_dispatch ();
--- 310,334 ----
      bool enable_step_trap_p;
      cpu_trace_stream trace_stream;
 
!     virtual void step_pin_handler (sid::host_int_4 tick)
        {
  recursion_record limit (& this->step_limit);
  if (UNLIKELY(! limit.ok())) return;
 
  this->yield_p = false;
+ this->current_tick = tick;
+
+ // Executing backward?
+ if (UNLIKELY (exec_direction == "backward"))
+  {
+    step_backward ();
+    this->stepped (1);
+    return;
+  }
+
+ this->current_step_insn_count = 0;
+ if (UNLIKELY (reversible_p))
+  this->init_change_logging ();
 
  // Check for triggerpoints due right now; may set yield_p!
  this->triggerpoint_manager.check_and_dispatch ();
*************** namespace sidutil
*** 339,346 ****
--- 354,428 ----
   insn_cycles >= max_num_cycles ? max_num_cycles :
   insn_cycles;
 
+ if (UNLIKELY (reversible_p))
+  this->finish_change_logging ();
  this->stepped (num_cycles);
        }
+
+     virtual void step_backward ()
+       {
+ // Make sure the infrastructure for reverse execution is in place.
+ if (UNLIKELY (! sim_sched || ! reversible_p))
+  {
+    std::cerr << "unable to execute in reverse" << endl;
+    this->signal_trap (cpu_trap_breakpoint, 0);
+    return;
+  }
+
+ // Check whether we're at the start of the program.
+ if (UNLIKELY (change_log_end <= change_log_begin))
+  {
+    // We're at the start of the program. See if there are previous
+    // instances to step backward into.
+    if (change_log_boundaries.empty ())
+      {
+ std::cerr << "No previous program instances to return to" << endl;
+ this->signal_trap (cpu_trap_breakpoint, 0);
+ return;
+      }
+    std::cerr << "Stepping back into the previous program instance" << endl;
+
+    // Switch to the previous program instance.
+    change_log_begin = change_log_boundaries.back ();
+    change_log_boundaries.pop_back ();
+    assert (change_log_begin < change_log_end);
+  }
+
+ // Unwind the change log until a triggerpoint is reached.
+ //
+ bool single_stepping = this->enable_step_trap_p;
+ while (change_log_end > change_log_begin)
+  {
+    // Restore the state to the previous tick.
+    --this->current_tick;
+    restore_state_to_time (this->current_tick);
+
+    // We've restored all the changes which take us back to the start of the
+    // previous insn.  Now notify the scheduler to reset the rest of
+    // the system to this time.
+    if (LIKELY (sim_sched))
+      sim_sched->set_attribute_value ("time", make_numeric_attribute (this->current_tick));
+
+    // Check for single stepping.
+    if (single_stepping)
+      {
+ this->signal_trap (sidutil::cpu_trap_stepped);
+ break;
+      }
+
+    // Check for triggerpoints due right now; may set yield_p!
+    this->triggerpoint_manager.check_and_dispatch ();
+    if (this->yield_p)
+      break;
+  }
+
+ // Let GDB know if we run out of state to reverse.
+ if (UNLIKELY (change_log_end <= change_log_begin))
+  {
+    std::cerr << "Program start reached while executing in reverse" << endl;
+    this->signal_trap (cpu_trap_breakpoint, 0);
+  }
+       }
      void yield ()
        {
  this->yield_p = true;
*************** namespace sidutil
*** 504,510 ****
    private:
      callback_pin<basic_cpu> reset_pin;
      virtual void reset () = 0;
!     void reset_pin_handler(sid::host_int_4 v) { this->reset (); this->stepped(1); }
 
      // Flush internal abstract icache (if any)
    private:
--- 586,603 ----
    private:
      callback_pin<basic_cpu> reset_pin;
      virtual void reset () = 0;
!     void reset_pin_handler(sid::host_int_4 v)
!     {
!       // If there's a change log, then start a new one.
!       if (change_log_end != 0)
! {
!  change_log_boundaries.push_back (change_log_begin);
!  change_log_begin = change_log_end;
! }
!       exec_direction = "forward";
!       this->reset ();
!       this->stepped(1);
!     }
 
      // Flush internal abstract icache (if any)
    private:
*************** namespace sidutil
*** 719,724 ****
--- 812,893 ----
   }
        }
 
+     // Reversible implementation
+   protected:
+     sidutil::change_log change_log;
+     unsigned change_log_begin;
+     unsigned change_log_end;
+     vector<unsigned> change_log_boundaries;
+     std::string change_string;
+     string exec_direction;
+     component *sim_sched;
+     sid::host_int_4 current_tick;
+     sid::host_int_4 last_tick;
+
+     virtual void init_change_logging () {}
+     virtual void finish_change_logging () {}
+
+     // Log any changes since the last change was logged.  Target specific
+     // changes are logged in change_log.finish ().
+     virtual void log_change (const void* change, sid::host_int_4 length)
+       {
+ assert (reversible_p);
+ change_log.push (& current_tick, sizeof (current_tick));
+ change_log.add (change, length);
+ change_log.finish ();
+ ++change_log_end;
+       }
+
+     // Restore the state represented by the given change log record.
+     virtual void restore_change (const char* record, sid::host_int_4 length)
+       {
+       }
+
+     // Restore our state to what it was at the given time.
+     virtual void restore_state_to_time (sid::host_int_4 tick)
+       {
+ // Call up to the base class.
+ reversible_component::restore_state_to_time (tick);
+
+ // Nothing to restore?
+ if (UNLIKELY (change_log_end == 0))
+  return;
+
+ // Rewind the change log to the given time.
+ unsigned found = change_log_end;
+ while (change_log_end >= 1)
+  {
+    // Obtain the most recent change log record.
+    sid::host_int_4 length;
+    const char *record = (const char *)change_log.top (length);
+
+    // The first item in the record is the time (tick) of the change.
+    // If it's before our target time, then we're done.
+    sid::host_int_4 new_tick = *(sid::host_int_4*)record;
+    if (new_tick < tick)
+      break;
+
+    record += sizeof (new_tick);
+    length -= sizeof (new_tick);
+
+    // Restore the state represented by the record.
+    restore_change (record, length);
+
+    // We're done with this record.
+    change_log.pop ();
+    --change_log_end;
+
+    // Adjust program instance boundaries, if necessary.
+    if (change_log_end < change_log_begin)
+      {
+ assert (! change_log_boundaries.empty ());
+ change_log_begin = change_log_boundaries.back ();
+ change_log_boundaries.pop_back ();
+ assert (change_log_end >= change_log_begin);
+      }
+  }
+       }
+
      virtual component::status dynamic_config(const string& spec)
        {
  // Call up to the base class
*************** public:
*** 930,936 ****
        gprof_unconfigured_p (false),
        gprof_prev_cycle (0),
        core_probe (0),
!       main (0)
        {
  // buses
  this->data_bus = 0;
--- 1099,1112 ----
        gprof_unconfigured_p (false),
        gprof_prev_cycle (0),
        core_probe (0),
!       main (0),
!       change_log (),
!       change_log_begin (0),
!       change_log_end (0),
!       change_log_boundaries (),
!       last_tick (~(sid::host_int_4)0),
!       exec_direction ("forward"),
!       sim_sched (0)
        {
  // buses
  this->data_bus = 0;
*************** public:
*** 997,1002 ****
--- 1173,1180 ----
  add_attribute_notify ("final-insn-count?", & this->final_insn_count_p, this,
       & basic_cpu::update_final_insn_count_p,
       "setting");
+ add_attribute ("exec-direction", &exec_direction, "setting");
+ add_uni_relation("sim-sched", &this->sim_sched);
 
  // For dynamic configuration
  add_uni_relation("gprof", &this->gprof);
Index: sid/include/sidmiscutil.h
===================================================================
RCS file: /cvs/src/src/sid/include/sidmiscutil.h,v
retrieving revision 1.8
diff -c -p -r1.8 sidmiscutil.h
*** sid/include/sidmiscutil.h 17 Dec 2003 19:51:02 -0000 1.8
--- sid/include/sidmiscutil.h 15 Sep 2006 20:27:53 -0000
***************
*** 1,6 ****
  // sidmiscutil.h - Useful utility classes.  -*- C++ -*-
 
! // Copyright (C) 1999-2003 Red Hat.
  // This file is part of SID and is licensed under the GPL.
  // See the file COPYING.SID for conditions for redistribution.
 
--- 1,6 ----
  // sidmiscutil.h - Useful utility classes.  -*- C++ -*-
 
! // Copyright (C) 1999-2003, 2006 Red Hat.
  // This file is part of SID and is licensed under the GPL.
  // See the file COPYING.SID for conditions for redistribution.
 
*************** namespace sidutil
*** 531,536 ****
--- 531,614 ----
      }
    };
 
+   // This class is intended for the implementation of change logging and
+   // change reversing. It is optimized for a potentially large number of
+   // elements and for growth, shrinkage and access as a LIFO stack. It is
+   // also optimized for growing again after shrinking.
+   class change_log
+   {
+   public:
+     change_log (sid::host_int_4 g = 0x100000) :
+       growth_rate (g),
+       buffer (0),
+       buf_index (0),
+       buf_size (0),
+       current_length (0)
+     {}
+    ~change_log ()
+     {
+       if (buffer)
+ delete buffer;
+     }
+
+     // Begin a new record and add the given data.
+     void push (const void *data, sid::host_int_4 length)
+     {
+       current_length = 0;
+       add (data, length);
+     }
+
+     // Add the given data to the current record.
+     void add (const void *data, sid::host_int_4 length)
+     {
+       if (buf_index + length > buf_size)
+ {
+  buf_size += growth_rate * length;
+  unsigned char* new_buf = new unsigned char[buf_size];
+  if (buffer)
+    {
+      memcpy (new_buf, buffer, buf_index);
+      delete buffer;
+    }
+  buffer = new_buf;
+ }
+
+       memcpy (buffer + buf_index, data, length);
+       buf_index += length;
+       current_length += length;
+     }
+
+     // Complete the current record by writing its length.
+     void finish ()
+     {
+       unsigned char l = current_length;
+       add (& l, 1);
+     }
+
+     // Remove the last record.
+     void pop ()
+     {
+       unsigned len = buffer[--buf_index];
+       buf_index -= len;
+     }
+
+     // Return a pointer to the last record.
+     const void *top (sid::host_int_4 &length) const
+     {
+       length = buffer[buf_index - 1];
+       return buffer + buf_index - length - 1;
+     }
+
+     // Is the change log emtpy?
+     bool empty () const { return buf_index <= 0; }
+
+   private:
+     sid::host_int_4 growth_rate;
+     unsigned char* buffer;
+     sid::host_int_4 buf_index;
+     sid::host_int_4 buf_size;
+     unsigned char current_length;
+   };
  }
 
  #endif // SIDMISCUTIL_H
Index: sid/main/dynamic/commonCfg.cxx
===================================================================
RCS file: /cvs/src/src/sid/main/dynamic/commonCfg.cxx,v
retrieving revision 1.16
diff -c -p -r1.16 commonCfg.cxx
*** sid/main/dynamic/commonCfg.cxx 14 Jul 2006 19:45:51 -0000 1.16
--- sid/main/dynamic/commonCfg.cxx 15 Sep 2006 20:27:53 -0000
*************** CpuCfg::CpuCfg (const string name,
*** 346,351 ****
--- 346,352 ----
      sess->sim_sched->add_subscription
      (this, "step!", "step-cycles",
       "time-query", "time-high", "time-low");
+   relate (this, "sim-sched", sess->sim_sched);
  }
 
 
*************** SessionCfg::SessionCfg (const string nam
*** 581,586 ****
--- 582,588 ----
      tcl_bridge (NULL),
      loader (NULL),
      verbose (false),
+     reversible_p (false),
      use_stdio (true),
      need_gprof (false),
      need_core_probe (false),
*************** void SessionCfg::write_load (Writer &w)
*** 659,664 ****
--- 661,678 ----
        host_sched->set_time (n, 150);
        use_stdio = false;
      }
+
+   // Setup all memory regions to be reversible, if specified.
+   if (reversible_p)
+     for (vector<MemCfg *>::iterator it = memory.begin ();
+ it != memory.end ();
+ ++it)
+       {
+ set (*it, "reversible?", "true");
+ relate (*it, "sim-sched", sim_sched);
+ conn_pin (sim_sched, "time-set", *it, "restore-to-time!");
+       }
+
    AggregateCfg::write_load (w);
  }
 
*************** void BoardCfg::write_config (Writer &w)
*** 1360,1365 ****
--- 1374,1386 ----
   PinConnection (cpu, "trap-code", gdb, "trap-code").write_to(w);
  }
      }  
+
+   // Set up the cpu to be reversible, if requested.
+   if (sess->reversible_p)
+     {
+       Setting (cpu, "reversible?", "true").write_to (w);
+       PinConnection (sess->sim_sched, "time-set", cpu, "restore-to-time!").write_to(w);
+     }
  }
 
  void BoardCfg::set_gprof (const string filename, gprof_type type, int interval)
Index: sid/main/dynamic/commonCfg.h
===================================================================
RCS file: /cvs/src/src/sid/main/dynamic/commonCfg.h,v
retrieving revision 1.10
diff -c -p -r1.10 commonCfg.h
*** sid/main/dynamic/commonCfg.h 11 May 2006 20:27:02 -0000 1.10
--- sid/main/dynamic/commonCfg.h 15 Sep 2006 20:27:53 -0000
*************** struct SessionCfg :
*** 233,238 ****
--- 233,239 ----
    void use_tksm();
    void use_tcl_bridge();
    void use_no_stdio ();
+   void set_reversible () { reversible_p = true; }
    virtual void set_loader (LoaderCfg *l);
    LoaderCfg *get_loader () const { return loader; }
    AtomicCfg *audio;
*************** struct SessionCfg :
*** 241,246 ****
--- 242,248 ----
    AtomicCfg *tcl_bridge;
    bool verbose;
    bool use_stdio;
+   bool reversible_p;
    bool need_gprof;
    bool need_core_probe;
    void add_ulog_file (const string filename);
*************** struct SessionCfg :
*** 248,253 ****
--- 250,257 ----
    map<const string, AtomicCfg *> ulog_map;
    void add_gdb () { ++gdb_count; }
    void add_board (ComponentCfg *b) { ++board_count; add_child (b); }
+   void add_memory (MemCfg *mem) { memory.push_back (mem); }
+   vector<MemCfg *> memory;
    virtual void write_config (Writer &w);
    // Support for dynamic configuration profiles
    vector<AtomicCfg *> wrapped_components;
Index: sid/main/dynamic/mainDynamic.cxx
===================================================================
RCS file: /cvs/src/src/sid/main/dynamic/mainDynamic.cxx,v
retrieving revision 1.8
diff -c -p -r1.8 mainDynamic.cxx
*** sid/main/dynamic/mainDynamic.cxx 23 Aug 2005 21:09:48 -0000 1.8
--- sid/main/dynamic/mainDynamic.cxx 15 Sep 2006 20:27:53 -0000
***************
*** 1,6 ****
  // mainDynamic.cxx - high-tech mainline.  -*- C++ -*-
 
! // Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 Red Hat.
  // This file is part of SID and is licensed under the GPL.
  // See the file COPYING.SID for conditions for redistribution.
 
--- 1,6 ----
  // mainDynamic.cxx - high-tech mainline.  -*- C++ -*-
 
! // Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 Red Hat.
  // This file is part of SID and is licensed under the GPL.
  // See the file COPYING.SID for conditions for redistribution.
 
*************** usage ()
*** 73,78 ****
--- 73,79 ----
    cout << "--profile-config=NAME,OPTIONS" << endl;
    cout << "                      Specify options for a named profiling configuration" << endl;
    cout << "--rc                  Pass stop code as simulator exit rc" << endl;
+   cout << "--reversible          Configure for reversible simulation" << endl;
    cout << "--save-temps=FILE     Write config to FILE, '-' for stdout." << endl;
    cout << "--wrap=COMPONENT      Turn on SID API tracing for COMPONENT" << endl;
    cout << "--verbose             Turn on run-time verbosity settings" << endl;
*************** void try_add_memory (const string memspe
*** 412,417 ****
--- 413,420 ----
        if (! (mmap_p || read_only_p))
  sess->shutdown_seq->add_output (6, mem, "image-store");
      }
+
+   sess->add_memory (mem);
  }
 
 
*************** main(int argc, char* argv[])
*** 549,555 ****
    enum option_num { opt_help, opt_version, opt_save_temps, opt_wrap,
     opt_verbose, opt_tksched, opt_enable_warnings,
     opt_persistent, opt_profile_config,
!    opt_rc, opt_no_run, opt_sidrtc, opt_sidcodec,
     opt_tksm, opt_board, opt_cpu, opt_gdb, opt_gloss, opt_engine,
     opt_insn_count, opt_load, opt_icache, opt_dcache,
     opt_memory_region, opt_profile_func,
--- 552,558 ----
    enum option_num { opt_help, opt_version, opt_save_temps, opt_wrap,
     opt_verbose, opt_tksched, opt_enable_warnings,
     opt_persistent, opt_profile_config,
!    opt_rc, opt_reversible, opt_no_run, opt_sidrtc, opt_sidcodec,
     opt_tksm, opt_board, opt_cpu, opt_gdb, opt_gloss, opt_engine,
     opt_insn_count, opt_load, opt_icache, opt_dcache,
     opt_memory_region, opt_profile_func,
*************** main(int argc, char* argv[])
*** 578,583 ****
--- 581,587 ----
      {"persistent",      no_argument, & curr_opt, opt_persistent },
      {"profile-config",  required_argument, &curr_opt, opt_profile_config },
      {"rc",              no_argument, & curr_opt, opt_rc },
+     {"reversible",      no_argument, & curr_opt, opt_reversible },
      {"tksm",            no_argument, & curr_opt, opt_tksm },
 
 
*************** main(int argc, char* argv[])
*** 880,885 ****
--- 884,907 ----
       rc_p = true;
       break;
 
+    case opt_reversible:
+      if (sess)
+ {
+  sess->set_reversible ();
+  // --insn-count must be 1 for this to work correctly
+  if (curr_board)
+    {
+      curr_board->set_step_insn_count("1");
+      board_start_config += " --insn-count=1";
+    }
+  else
+    {
+      defaults.step_insn_count = "1";
+      defaults.start_config += " --insn-count=1";
+    }
+ }
+      break;
+
     case opt_sidrtc:
       option_requires_board (curr_board, "sidrtc");
       curr_board->add_sidrtc (optaddr("sidrtc"));


Index: cgen/cpu/xstormy16.cpu
===================================================================
RCS file: /cvs/src/src/cgen/cpu/xstormy16.cpu,v
retrieving revision 1.12
diff -c -p -r1.12 xstormy16.cpu
*** cgen/cpu/xstormy16.cpu 22 Jul 2004 01:49:27 -0000 1.12
--- cgen/cpu/xstormy16.cpu 15 Sep 2006 20:27:47 -0000
***************
*** 1,5 ****
  ; xstormy16 CPU core description. -*- Scheme -*-
! ; Copyright (C) 2001, 2002, 2003 Red Hat, Inc.
  ; This file is part of CGEN.
  ; See file COPYING.CGEN for details.
 
--- 1,5 ----
  ; xstormy16 CPU core description. -*- Scheme -*-
! ; Copyright (C) 2001, 2002, 2003, 2006 Red Hat, Inc.
  ; This file is part of CGEN.
  ; See file COPYING.CGEN for details.
 
***************
*** 68,74 ****
 
  ; Hardware elements.
 
! (dsh h-pc "program counter" (PC) (pc))
 
  (define-keyword
    (name gr-names)
--- 68,80 ----
 
  ; Hardware elements.
 
! (define-hardware
!   (name h-pc)
!   (comment "program counter")
!   (attrs PC)
!   (type pc)
!   (set (newval) (c-call "h_pc_set_handler" newval))
! )
 
  (define-keyword
    (name gr-names)
***************
*** 92,98 ****
    (type register WI (16))
    (indices extern-keyword gr-names)
    (get (index) (and #xFFFF (raw-reg h-gr index)))
!   (set (index newval) (set (raw-reg h-gr index) (and #xFFFF newval)))
  )
 
  (define-hardware
--- 98,104 ----
    (type register WI (16))
    (indices extern-keyword gr-names)
    (get (index) (and #xFFFF (raw-reg h-gr index)))
!   (set (index newval) (c-call "h_gr_set_handler" index newval))
  )
 
  (define-hardware
Index: sid/component/cgen-cpu/xstormy16/xstormy16.cxx
===================================================================
RCS file: /cvs/src/src/sid/component/cgen-cpu/xstormy16/xstormy16.cxx,v
retrieving revision 1.3
diff -c -p -r1.3 xstormy16.cxx
*** sid/component/cgen-cpu/xstormy16/xstormy16.cxx 18 Feb 2003 22:57:29 -0000 1.3
--- sid/component/cgen-cpu/xstormy16/xstormy16.cxx 15 Sep 2006 20:27:52 -0000
***************
*** 1,7 ****
  // xstormy16.cxx - Implementations of hand-written functions for the Xstormy16
  // simulator. -*- C++ -*-
 
! // Copyright (C) 2000 Red Hat.
  // This file is part of SID and is licensed under the GPL.
  // See the file COPYING.SID for conditions for redistribution.
 
--- 1,7 ----
  // xstormy16.cxx - Implementations of hand-written functions for the Xstormy16
  // simulator. -*- C++ -*-
 
! // Copyright (C) 2000, 2006 Red Hat.
  // This file is part of SID and is licensed under the GPL.
  // See the file COPYING.SID for conditions for redistribution.
 
*************** xstormy16_cpu::parity (int reg)
*** 363,365 ****
--- 363,507 ----
    tmp ^= tmp >> 1;
    return tmp & 1;
  }
+
+ // Called before execution of an insn.  Perform any tasks associated with
+ // logging changes in the state of this cpu during execution of an insn.
+ void
+ xstormy16_cpu_cgen::init_change_logging ()
+ {
+   // Call up to the base class.
+   cgen_bi_endian_cpu::init_change_logging ();
+
+   // We will be tracking changes to the pc and to the gr registers.
+   gr_changed = 0;
+   pc_changed = PC_UNCHANGED;
+ }
+
+ // Called after execution of an insn.  Log the changes which occurred during
+ // execution of the insn.
+ void
+ xstormy16_cpu_cgen::finish_change_logging ()
+ {
+   // Call up to the base class.
+   cgen_bi_endian_cpu::finish_change_logging ();
+
+   // The largest possible record would contain:
+   //   1 byte representing the type of pc change
+   //   A 16 bit mask indicating which gr registers changed
+   //   Possibly the old pc value
+   //   Old gr register values (if any)
+   char buffer[1 + 2 + sizeof (old_h_pc) + sizeof (old_h_gr)];
+
+   // Record the nature of any pc or gr changes.
+   *(sid::host_int_1 *)(buffer + 0) = pc_changed;
+   *(sid::host_int_2 *)(buffer + 1) = gr_changed;
+
+   // If the pc change was too large to represent in the byte written above, then
+   // record the old pc value.
+   unsigned bufix = 3;
+   if (pc_changed == PC_RESET)
+     {
+       *(USI *)(buffer + bufix) = old_h_pc;
+       bufix += sizeof (old_h_pc);
+     }
+
+   // Record the old values of any gr registers that changed.
+   sid::host_int_2 mask = 1;
+   for (int i = 0; i < 16; ++i)
+     {
+       if ((gr_changed & mask) != 0)
+ {
+  *(SI *)(buffer + bufix) = old_h_gr[i];
+  bufix += sizeof (old_h_gr[i]);
+  // std::cout << ' ' << std::hex << old_h_gr[i] << std::dec;
+ }
+       mask <<= 1;
+     }
+
+   // Save the change log record we have just created.
+   log_change (buffer, bufix);
+ }
+
+ // Keep track of any changes to the pc.
+ void
+ xstormy16_cpu_cgen::log_pc_change (USI new_pc)
+ {
+   // If this is the first change, then save the original pc value.
+   if (LIKELY (pc_changed == PC_UNCHANGED))
+     old_h_pc = this->hardware.h_pc;
+
+   // Most of the time, pc changes are small enough to represented by one byte.
+   // Save these changes in 'pc_changed'.
+   //
+   // The value of PC_UNCHANGED is zero and so this condition will be represented
+   // with no special handling.
+   //
+   // The special value PC_RESET is an odd number and cannot occur under normal
+   // circumstances.  It indicates a larger pc change.
+   SI diff = new_pc - old_h_pc;
+   if (LIKELY (-128 <= diff && diff <= 127))
+     pc_changed = diff;
+   else
+     pc_changed = PC_RESET;
+ }
+
+ // Keep track of any changes to gr registers.  It is only necessary to
+ // save the original value of a register the first time it is changed.
+ void
+ xstormy16_cpu_cgen::log_gr_change (UINT regno, SI newval)
+ {
+   if (! (gr_changed & (1 << regno)))
+     {
+       gr_changed |= 1 << regno;
+       old_h_gr[regno] = this->hardware.h_gr[regno];
+     }
+ }
+
+ // Given a change log record, restore the state that it represents.
+ void
+ xstormy16_cpu_cgen::restore_change (const char *data, sid::host_int_4 length)
+ {
+   // The first byte indicates the nature of any change to the pc.
+   assert (length >= 1);
+   sid::signed_host_int_1 pc_changed = *(sid::host_int_1 *)data;
+   ++data;
+   --length;
+
+   // The second two bytes are a bit mask indicating changes to gr registers.
+   assert (length >= 2);
+   sid::host_int_2 gr_changed = *(sid::host_int_2 *)data;
+   data += 2;
+   length -= 2;
+
+   // Restore the pc.
+   if (UNLIKELY (pc_changed == PC_RESET))
+     {
+       // For large changes to the pc, the previous pc value is recorded in the
+       // change log record.
+       this->hardware.h_pc = *(USI *)data;
+       data += sizeof (USI);
+       length -= sizeof (USI);
+     }
+   else
+     {
+       // Smaller changes to the pc are represented by a one byte delta.
+       this->hardware.h_pc -= pc_changed;
+     }
+
+   // Restore any gr registers which changed.
+   sid::host_int_2 mask = 1;
+   for (int i = 0; i < 16; ++i)
+     {
+       if ((gr_changed & mask) != 0)
+ {
+  assert (length >= sizeof (SI));
+  this->hardware.h_gr[i] = *(SI *)data;
+  data += sizeof (SI);
+  length -= sizeof (SI);
+ }
+       mask <<= 1;
+     }
+
+   // The entire record must be consumed.
+   assert (length == 0);
+ }
Index: sid/component/cgen-cpu/xstormy16/xstormy16.h
===================================================================
RCS file: /cvs/src/src/sid/component/cgen-cpu/xstormy16/xstormy16.h,v
retrieving revision 1.3
diff -c -p -r1.3 xstormy16.h
*** sid/component/cgen-cpu/xstormy16/xstormy16.h 26 Jun 2006 21:06:41 -0000 1.3
--- sid/component/cgen-cpu/xstormy16/xstormy16.h 15 Sep 2006 20:27:52 -0000
***************
*** 1,6 ****
  // xstormy16.h - Hand-written code for the Sanyo Xstormy16 CPU. -*- C++ -*-
 
! // Copyright (C) 1999, 2000 Red Hat.
  // This file is part of SID and is licensed under the GPL.
  // See the file COPYING.SID for conditions for redistribution.
 
--- 1,6 ----
  // xstormy16.h - Hand-written code for the Sanyo Xstormy16 CPU. -*- C++ -*-
 
! // Copyright (C) 1999, 2000, 2006 Red Hat.
  // This file is part of SID and is licensed under the GPL.
  // See the file COPYING.SID for conditions for redistribution.
 
***************
*** 11,32 ****
 
  namespace xstormy16
  {
!   class xstormy16_cpu_cgen
      {
        // Include cgen generated elements.
  #include "xstormy16-cpu.h"
 
      public:
        inline void cgen_rtx_error(const char* msg) const
  {
   cerr << "xstormy16-cpu rtx error: " << msg << endl;
  }
      protected:
        USI syscall_trap_num;
 
      };
 
!   class xstormy16_cpu: public xstormy16_cpu_cgen, public cgen_bi_endian_cpu
      {
      private:
        scache_engine<xstormy16_scache> engine;
--- 11,72 ----
 
  namespace xstormy16
  {
!   class xstormy16_cpu_cgen: public cgen_bi_endian_cpu
      {
        // Include cgen generated elements.
  #include "xstormy16-cpu.h"
 
      public:
+       xstormy16_cpu_cgen () {}
+       ~xstormy16_cpu_cgen () throw() { };
+
+     protected:
+       // Log any changes to the pc, if we're reversible.
+       void h_pc_set_handler (USI newval)
+         {
+  if (UNLIKELY (this->reversible_p))
+    log_pc_change (newval);
+  this->hardware.h_pc = newval;
+         }
+
+       // Log any changes to the gr registers, if we're reversible.
+       void h_gr_set_handler (UINT regno, SI newval)
+         {
+  if (UNLIKELY (this->reversible_p))
+    log_gr_change (regno, newval);
+  this->hardware.h_gr[regno] = (0xffff & newval);
+         }
+
+       // Stateful (reversible) component implementation methods.
+       virtual void init_change_logging ();
+       virtual void finish_change_logging ();
+
+       void log_pc_change (USI new_pc);
+       void log_gr_change (UINT regno, SI newval);
+
+       virtual void restore_change (const char *data, sid::host_int_4 length);
+
+     protected:
        inline void cgen_rtx_error(const char* msg) const
  {
   cerr << "xstormy16-cpu rtx error: " << msg << endl;
  }
+
      protected:
        USI syscall_trap_num;
 
+       // The values of these constants are significant and must not be changed.
+       static const sid::signed_host_int_1 PC_UNCHANGED = 0x00;
+       static const sid::signed_host_int_1 PC_RESET = 0x01;
+
+       // State for change logging.
+       sid::host_int_2 gr_changed;
+       sid::signed_host_int_1 pc_changed;
+       SI old_h_gr[16];
+       USI old_h_pc;
      };
 
!   class xstormy16_cpu: public xstormy16_cpu_cgen
      {
      private:
        scache_engine<xstormy16_scache> engine;

Reply | Threaded
Open this post in threaded view
|

Re: [patch] Reverse Execution in SID, Reverse Debugging with GDB and SID

Dave Brolley-2
Micheal Snyder is going to be doing some more work on revserse
debugging/execution and he reminded me that I never did commit this
work. It's committed now.

Dave

Dave Brolley wrote:

> Hi,
>
> The attached patches implement
>
> 1) Infrastructure for reverse execution of in SID
> 2) Target specific implementation for xstormy16
>
> This work is intended to be used in conjunction with Michael Snyder's
> work on reverse debugging in GDB, but I suppose that the idea of
> executing in reverse need not necessarily be restricted only to this
> purpose.
>
> I'm submitting the general infrastructure work for approval to commit.
> The xstormy16 specific implementation was done mainly as an example,
> and perhaps should also be committed.
>
> Here is an overview of the changes and how they work:
>
> o The changes the the scheduling components are mainly to have them
> drive the value of 'now' for each scheduled event. Currently they
> drive the value zero which is not used on the receiving end. This
> provides a mechanism for a scheduled component to know what 'time'
> each scheduled event occurs. Components, like memory, which are not
> scheduled, but which need to know at what 'time' an event occurred can
> query the scheduler's time attribute to obtain the same information.  
> The scheduler's set_time method has also been updated so that it
> cancels all pending events. This change was probably already
> necessary, but the need was exposed during testing of this feature.
>
> o A new component class mix-in called reversible_component has been
> created to consolidate the common needs of a component which may have
> to step back in 'time'. The main features are a reversible? attribute
> which tells the component that it should be keeping track of events
> and a restore-to-time! pin which tells the component to restore it's
> state to a given 'time'.
>
> o A new utility class called change_log has been added to aid those
> components which choose to implement this using change logging
> techniques.
>
> o The basic_cpu class has been given specific knowledge of what it
> means to execute in reverse. This is controlled by it's new
> exec-direction attribute which can be set to "forward" or "backward".
> This attribute is checked when the step-insns pin is driven. If the
> direction is forward, then it's business as usual. If it's backward,
> then the cpu does what is necessary to step backward and then resets
> the scheduler to that 'time'. The scheduler in turn drives its
> time-set pin to notify other components in the system to restore
> themselves to that 'time'. Note that reverse execution need not be one
> insn at a time. I did this for the xstormy16 example so that stepi
> would work in reverse with gdb. Note that in the xstormy16 example,
> breakpoints and watchpoints are also supported while executing in
> reverse.
>
> The idea is that components in the system which have state implement
> some method of restoring their state to what it was at a given 'time'.
> Whatever makes the most sense for each particular component. The cpu
> is an obvious example as is memory. More complex systems may have
> other components with this requirement. For the xstormy16, cpu and
> memory are the only components requiring this capability.
>
> In particular, the xstormy16 cpu component needed only to track
> changes in the pc and the gr registers. Using specific knowlege of the
> kinds of changes that are possible to the pc, in particular I was able
> to implement a change logging system that uses only 1 byte for small
> pc changes (i.e. less than 128 bytes) and 3 bytes otherwise. Similarly
> for the gr registers, a 2 byte mask indicates which registers have
> changed and then only the changed registers are added to the change
> log record. Many change log records are therefore 5 bytes or less.
>
> One interesting 'feature' of the current implementation is that if a
> program has been debugged to completion and then debugging has started
> again (i.e. the gdb 'target' command establishes a new connection with
> SID), one can debug backward past the beginning of the program (with a
> warning from SID) and back into the previous execution instance. The
> feature is handy in the case that you use the GDB continue command and
> end up at the end of the program by mistake.
>
> Comments, ideas, and, of course, approval to commit the infrastructure
> patch please. I can also commit the xstormy16 specific parts if desired.
>
> Dave
>
Reply | Threaded
Open this post in threaded view
|

Re: [patch] Reverse Execution in SID, Reverse Debugging with GDB and SID

Michael Snyder-3
On Tue, 2008-06-17 at 14:23 -0400, Dave Brolley wrote:
> Micheal Snyder is going to be doing some more work on revserse
> debugging/execution and he reminded me that I never did commit this
> work. It's committed now.

Woo hoo, so now there is at least one target (xstormy16)
where we can actually *test* reverse debugging.  Should
be a good step toward committing it to gdb.

> Dave Brolley wrote:
> > Hi,
> >
> > The attached patches implement
> >
> > 1) Infrastructure for reverse execution of in SID
> > 2) Target specific implementation for xstormy16
> >
> > This work is intended to be used in conjunction with Michael Snyder's
> > work on reverse debugging in GDB, but I suppose that the idea of
> > executing in reverse need not necessarily be restricted only to this
> > purpose.
> >
> > I'm submitting the general infrastructure work for approval to commit.
> > The xstormy16 specific implementation was done mainly as an example,
> > and perhaps should also be committed.
> >
> > Here is an overview of the changes and how they work:
> >
> > o The changes the the scheduling components are mainly to have them
> > drive the value of 'now' for each scheduled event. Currently they
> > drive the value zero which is not used on the receiving end. This
> > provides a mechanism for a scheduled component to know what 'time'
> > each scheduled event occurs. Components, like memory, which are not
> > scheduled, but which need to know at what 'time' an event occurred can
> > query the scheduler's time attribute to obtain the same information.  
> > The scheduler's set_time method has also been updated so that it
> > cancels all pending events. This change was probably already
> > necessary, but the need was exposed during testing of this feature.
> >
> > o A new component class mix-in called reversible_component has been
> > created to consolidate the common needs of a component which may have
> > to step back in 'time'. The main features are a reversible? attribute
> > which tells the component that it should be keeping track of events
> > and a restore-to-time! pin which tells the component to restore it's
> > state to a given 'time'.
> >
> > o A new utility class called change_log has been added to aid those
> > components which choose to implement this using change logging
> > techniques.
> >
> > o The basic_cpu class has been given specific knowledge of what it
> > means to execute in reverse. This is controlled by it's new
> > exec-direction attribute which can be set to "forward" or "backward".
> > This attribute is checked when the step-insns pin is driven. If the
> > direction is forward, then it's business as usual. If it's backward,
> > then the cpu does what is necessary to step backward and then resets
> > the scheduler to that 'time'. The scheduler in turn drives its
> > time-set pin to notify other components in the system to restore
> > themselves to that 'time'. Note that reverse execution need not be one
> > insn at a time. I did this for the xstormy16 example so that stepi
> > would work in reverse with gdb. Note that in the xstormy16 example,
> > breakpoints and watchpoints are also supported while executing in
> > reverse.
> >
> > The idea is that components in the system which have state implement
> > some method of restoring their state to what it was at a given 'time'.
> > Whatever makes the most sense for each particular component. The cpu
> > is an obvious example as is memory. More complex systems may have
> > other components with this requirement. For the xstormy16, cpu and
> > memory are the only components requiring this capability.
> >
> > In particular, the xstormy16 cpu component needed only to track
> > changes in the pc and the gr registers. Using specific knowlege of the
> > kinds of changes that are possible to the pc, in particular I was able
> > to implement a change logging system that uses only 1 byte for small
> > pc changes (i.e. less than 128 bytes) and 3 bytes otherwise. Similarly
> > for the gr registers, a 2 byte mask indicates which registers have
> > changed and then only the changed registers are added to the change
> > log record. Many change log records are therefore 5 bytes or less.
> >
> > One interesting 'feature' of the current implementation is that if a
> > program has been debugged to completion and then debugging has started
> > again (i.e. the gdb 'target' command establishes a new connection with
> > SID), one can debug backward past the beginning of the program (with a
> > warning from SID) and back into the previous execution instance. The
> > feature is handy in the case that you use the GDB continue command and
> > end up at the end of the program by mistake.
> >
> > Comments, ideas, and, of course, approval to commit the infrastructure
> > patch please. I can also commit the xstormy16 specific parts if desired.
> >
> > Dave
> >