/* Copyright (C) 1998 T. Scott Dattalo This file is part of gpsim. gpsim 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, or (at your option) any later version. gpsim 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 gpsim; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __TRACE_H__ #define __TRACE_H__ #include #include #include "exports.h" #include "gpsim_classes.h" #include "trigger.h" #include "value.h" #include "registers.h" extern "C" { #include "lxt_write.h" } class Processor; class Trace; class TraceFrame; //======================================================================== // A 'TraceObject' is created when the contents of the Trace buffer are // displayed. The TraceObjects are placed into TraceFrames, and eventually // the entire frame is displayed. class TraceObject { public: TraceObject(); virtual void print(FILE *)=0; virtual void print_frame(TraceFrame *,FILE *); virtual void getState(TraceFrame *); // FIXME Is this even used? }; class CycleTraceObject : public TraceObject { public: CycleTraceObject(); virtual void print(FILE *); virtual void print_frame(TraceFrame *,FILE *); virtual void getState(TraceFrame *); }; class InvalidTraceObject : public TraceObject { public: InvalidTraceObject(int type); virtual void print(FILE *); protected: int mType; }; class ProcessorTraceObject : public TraceObject { public: Processor *cpu; ProcessorTraceObject(Processor *_cpu) : TraceObject() , cpu(_cpu) { } virtual void print(FILE *)=0; }; class ModuleTraceType; class ModuleTraceObject : public TraceObject { public: Module *pModule; ModuleTraceType *pModuleTraceType; unsigned int mTracedData; ModuleTraceObject(Module *_module, ModuleTraceType *pmtt, unsigned int d) : TraceObject() , pModule(_module), pModuleTraceType(pmtt), mTracedData(d) { } virtual void print(FILE *); }; class RegisterWriteTraceObject : public ProcessorTraceObject { public: Register *reg; RegisterValue from; RegisterValue to; RegisterWriteTraceObject(Processor *_cpu, Register *_reg, RegisterValue trv); virtual void print(FILE *); virtual void getState(TraceFrame *); }; class RegisterReadTraceObject : public RegisterWriteTraceObject { public: RegisterReadTraceObject(Processor *_cpu, Register *_reg, RegisterValue trv); virtual void print(FILE *); virtual void getState(TraceFrame *); }; class PCTraceObject : public ProcessorTraceObject { public: unsigned int address; PCTraceObject(Processor *_cpu, unsigned int _address); virtual void print(FILE *); virtual void print_frame(TraceFrame *,FILE *); }; //======================================================================== // The TraceType class is the base class for the various trace types that // are supported by gpsim. In general, when a trace type is created, // a 32 bit identifier is created. The upper byte of this identifier is // the "type" of the trace and is dynamically allocated. The lower 24 // bits of this identifer are 0. When a TraceType is traced, the lower // 24 bits are filled with the information that is to be recorded in // the trace buffer. The whole 32 bits are then written to the trace buffer // array. class TraceType { public: TraceType(unsigned int nTraceEntries); void setType(unsigned int t) { mType = t;} // The actual type of the TraceType is an 8-bit field in the // upper 32-bits of an unsigned integer. A TraceType can have // more than one type, although the types are consecutive. unsigned int type(unsigned int iType=0) { return (iType= (mType&tMask)) && ((aType&tMask) <= ((mType&tMask) + (mSize<<24))); } virtual int bitsTraced() { return 24; } virtual int dump_raw(Trace *,unsigned tbi, char *buf, int bufsize); private: unsigned int mType; // The integer type is dynamically // assigned by the Trace class. unsigned int mSize; // The number of positions this // type occupies }; class CycleTraceType : public TraceType { public: CycleTraceType(unsigned int nTraceEntries); virtual TraceObject *decode(unsigned int tbi); virtual bool isFrameBoundary(); virtual int dump_raw(Trace *,unsigned tbi, char *buf, int bufsize); virtual int entriesUsed(Trace *,unsigned int tbi); }; class ModuleTraceType : public TraceType { public: Module *pModule; const char *pDescription; ModuleTraceType(Module *_pModule, unsigned int nTraceEntries, const char *desc) : TraceType(nTraceEntries), pModule(_pModule), pDescription(desc) { } virtual TraceObject *decode(unsigned int tbi); virtual int dump_raw(Trace *,unsigned tbi, char *buf, int bufsize); }; class ProcessorTraceType : public TraceType { public: Processor *cpu; ProcessorTraceType(Processor *_cpu, unsigned int nTraceEntries) : TraceType(nTraceEntries), cpu(_cpu) { } virtual TraceObject *decode(unsigned int tbi) = 0; }; class PCTraceType : public ProcessorTraceType { public: PCTraceType(Processor *_cpu, unsigned int nTraceEntries); virtual TraceObject *decode(unsigned int tbi); virtual bool isFrameBoundary() { return true; } virtual int dump_raw(Trace *,unsigned tbi, char *buf, int bufsize); }; class RegisterWriteTraceType : public ProcessorTraceType { public: RegisterWriteTraceType(Processor *_cpu, unsigned int nTraceEntries); virtual TraceObject *decode(unsigned int tbi); virtual int dump_raw(Trace *,unsigned tbi, char *buf, int bufsize); }; class RegisterReadTraceType : public ProcessorTraceType { public: RegisterReadTraceType(Processor *_cpu, unsigned int nTraceEntries); virtual TraceObject *decode(unsigned int tbi); virtual int dump_raw(Trace *,unsigned tbi, char *buf, int bufsize); }; //======================================================================== // TraceFrame // // A trace frame collects all trace items that occurred at the same instant // of time. When the trace buffer is decoded, markers will be examined // to determine the frame boundaries. class TraceFrame { public: list traceObjects; guint64 cycle_time; TraceFrame(); virtual ~TraceFrame(); virtual void add(TraceObject *to); virtual void print(FILE *); virtual void update_state(void); }; //----------------------------------------------------------- class TraceRawLog { public: char *log_filename; FILE *log_file; void log(); void enable(char*); void disable(); TraceRawLog(); ~TraceRawLog(); }; //------------------------------------------------------------ class traceValue : public gpsimValue { public: traceValue(void); virtual void put_value(unsigned int new_value) {}; virtual unsigned int get_value(void); }; //--------------------------------------------------------- // Class for trace buffer class Trace { public: enum eTraceTypes { NOTHING = 0x3fffffff, BREAKPOINT = (2<<24), INTERRUPT = (3<<24), _RESET = (4<<24), WRITE_TRIS = (5<<24), OPCODE_WRITE = (7<<24), LAST_TRACE_TYPE = (8<<24), TYPE_MASK = (0xff<<24), CYCLE_COUNTER_LO = (0x80<<24), CYCLE_COUNTER_HI = (0x40<<24) }; #define TRACE_BUFFER_SIZE (1<<12) #define TRACE_BUFFER_MASK (TRACE_BUFFER_SIZE-1) #define TRACE_BUFFER_NEAR_FULL (TRACE_BUFFER_SIZE * 3 /4) #define TRACE_STRING_BUFFER 50 unsigned int trace_buffer[TRACE_BUFFER_SIZE]; unsigned int trace_index; unsigned int trace_flag; bool bLogging; TraceRawLog logger; traceValue trace_value; // When interfaced with a gui, the contents of the trace // buffer are decoded one line-at-a-time, copied to the string_buffer // and sent to the gui via xref interface (actually, the gui // is notified that new data is available in the string_buffer). XrefObject *xref; char string_buffer[TRACE_STRING_BUFFER]; guint64 string_cycle; // The cycle corresponding to the decoded string unsigned int string_index; // The trace buffer index corresponding " " Processor *cpu; TraceFrame *current_frame; guint64 current_cycle_time; // used when decoding the trace buffer. list traceFrames; unsigned int lastTraceType; unsigned int lastSubTraceType; Trace (void); ~Trace(void); // trace raw allows any value to be written to the trace buffer. // This is useful for modules that wish to trace things, but do // not wish to modify the Trace class. inline void raw (unsigned int ui) { trace_buffer[trace_index] = ui; trace_index = (trace_index + 1) & TRACE_BUFFER_MASK; } inline void opcode_write (unsigned int address, unsigned int opcode) { trace_buffer[trace_index] = OPCODE_WRITE | (address & 0xffffff); trace_index = (trace_index + 1) & TRACE_BUFFER_MASK; trace_buffer[trace_index] = OPCODE_WRITE | (opcode & 0xffff); trace_index = (trace_index + 1) & TRACE_BUFFER_MASK; } inline void cycle_counter (guint64 cc) { // The 64 bit cycle counter requires two 32 bit traces. trace_buffer[trace_index] = (unsigned int)(CYCLE_COUNTER_LO | (cc & 0xffffffff)); trace_index = (trace_index + 1) & TRACE_BUFFER_MASK; trace_buffer[trace_index] = (unsigned int)(CYCLE_COUNTER_HI | (cc>>32) | (cc & CYCLE_COUNTER_LO)); trace_index = (trace_index + 1) & TRACE_BUFFER_MASK; } inline void breakpoint (unsigned int bp) { trace_buffer[trace_index] = BREAKPOINT | bp; trace_index = (trace_index + 1) & TRACE_BUFFER_MASK; } void interrupt (void) { trace_buffer[trace_index] = INTERRUPT; trace_index = (trace_index + 1) & TRACE_BUFFER_MASK; } inline void reset (RESET_TYPE r) { trace_buffer[trace_index] = _RESET | ( (int) r); trace_index = (trace_index + 1) & TRACE_BUFFER_MASK; } inline void write_TRIS (unsigned int value) { trace_buffer[trace_index] = WRITE_TRIS | value; trace_index = (trace_index + 1) & TRACE_BUFFER_MASK; } inline bool near_full(void) { return (trace_index > TRACE_BUFFER_NEAR_FULL); } void switch_cpus(Processor *new_cpu) {cpu = new_cpu;}; int dump (int n=0, FILE *out_stream=0); void dump_last_instruction(void); int dump1(unsigned int,char *, int); void dump_raw(int n); // tbi - trace buffer index masking. inline unsigned int tbi(unsigned int index) { return index & TRACE_BUFFER_MASK; } // inRange - returns true if the trace index i is between the // indices of low and high. // It's assumed that the range does not exceed half of the trace buffer bool inRange(unsigned int i, unsigned int low, unsigned int high) { i = tbi(i); if( low < high) return (i >= low && i <= high); // Looks like the range straddles the roll over boundary. return (i >= low || i <= high); } // get() return the trace entry at 'index' inline unsigned int operator [] (unsigned int index) { return trace_buffer[tbi(index)]; } unsigned int get(unsigned int index) { return trace_buffer[tbi(index)]; } // type() - return the trace type at 'index' unsigned int type(unsigned int index); // A gpsim clock cycle takes two consecutive trace buffer entries. // The is_cycle_trace() member function will examine the trace // buffer to determine if the two traces starting at 'index' are // a cycle trace. int is_cycle_trace(unsigned int index, guint64 *cvt_cycle); // When logging is enabled, the entire trace buffer will be copied to a file. void enableLogging(char *fname); void disableLogging(); unsigned int allocateTraceType(TraceType *); // Trace frame manipulation void addFrame(TraceFrame *newFrame); void addToCurrentFrame(TraceObject *to); void deleteTraceFrame(void); void printTraceFrame(FILE *); }; #if defined(IN_MODULE) && defined(_WIN32) // we are in a module: don't access trace object directly! LIBGPSIM_EXPORT Trace & get_trace(void); #else // we are in gpsim: use of get_trace() is recommended, // even if trace object can be accessed directly. extern Trace trace; inline Trace &get_trace(void) { return trace; } #endif //----------------------------------------------------------- #define TRACE_FILE_FORMAT_ASCII 0 #define TRACE_FILE_FORMAT_LXT 1 class TraceLog : public TriggerObject { public: bool logging; bool lograw; int items_logged; char *log_filename; FILE *log_file; Processor *cpu; unsigned int last_trace_index; Trace buffer; int file_format; struct lt_trace *lxtp; struct lt_symbol *symp; TraceLog(void); ~TraceLog(void); virtual void callback(void); void enable_logging(const char *new_filename=0, int format=TRACE_FILE_FORMAT_ASCII); void disable_logging(void); void switch_cpus(Processor *new_cpu); void open_logfile(const char *new_fname, int format); void close_logfile(void); void write_logfile(void); void status(void); void lxt_trace(unsigned int address, unsigned int value, guint64 cc); void register_read(Register *, guint64 cc); void register_write(Register *, guint64 cc); void register_read_value(Register *, guint64 cc); void register_write_value(Register *, guint64 cc); }; #if defined(_WIN32) // we are in a module: don't access trace_log object directly! LIBGPSIM_EXPORT TraceLog & GetTraceLog(void); #else // we are in gpsim: use of GetTraceLog() is recommended, // even if trace_log object can be accessed directly. extern TraceLog trace_log; inline TraceLog &GetTraceLog(void) { return trace_log; } #endif //----------------------------------------------------------- class ProfileKeeper : public TriggerObject { public: bool enabled; Processor *cpu; unsigned int last_trace_index; unsigned int instruction_address; unsigned int trace_pc_value; ProfileKeeper(void); ~ProfileKeeper(void); void catchup(void); virtual void callback(void); void enable_profiling(void); void disable_profiling(void); void switch_cpus(Processor *new_cpu); }; extern ProfileKeeper profile_keeper; /********************************************************************** * boolean event logging * * The boolean event logger is a class for logging the time * of boolean (i.e. 0/1) events. * * The class is designed to be efficient for both logging events and * for accessing events that have already been logged. The events * are stored in several small buffers that are linked together with * binary trees. Each small buffer is linear, i.e. an array. Each * element of the array stores the time when the event occurred. * The state of the event is encoded in the position of the array. * In other words, "high" events are at the odd indices of the array * and "low" ones at the even ones. * * Each small buffer is associated with a contiguous time span. The * start and end of this span is recorded so that one can quickly * ascertain if a certain time instant resideds in the buffer. * * The binary tree is fairly standard. A single top node records three * numbers: the start time for the left child, the end time for the * left child (which by default is the start time for the right child) * and the end time for the right child. The nodes of left and right * children are similar to the parents. To find which small buffer * contains an event for a certain time, one simply starts at the * top of the tree and traverses the nodes until a leaf is reached. * A leaf, of course, is where the data is stored. * * The time for the event comes from gpsim's global cycle counter. * This counter is 64-bits wide. The buffers that store the time however, * are only 32-bits wide. There are two simple tricks employed to get * around this problem. First, full 64-bit time for the first event * is recorded. All subsequent events are 32-bit offsets from this. * Second, to ensure that the 32-bit offset does not wrap around, the * boolean event logger will set a cycle counter break point that is * less than 2^32 cycles in the future. If this break point is encountered * before the buffer fills, then this buffer is closed and added to the * binary and a new buffer is started. * * Repeated events are not logged. E.g.. if two 1's are logged, the * second one is ignored. * */ class BoolEventBuffer : public TriggerObject { public: guint32 index; // Index into the buffer guint64 *buffer; // Where the time is stored guint32 max_events; // Size of the event buffer guint64 start_time; // time of the first event guint64 future_cycle; // time at which the buffer can store no more data. bool bInitialState; // State when started. bool bActive; // True if the buffer is enabled for storing. bool bFull; // True if the buffer has been filled. BoolEventBuffer(bool _initial_state, guint32 _max_events = 4096); ~BoolEventBuffer(void); unsigned int get_index(guint64 event_time); void activate(bool _initial_state); void deactivate(void); void callback(void); void callback_print(void); inline bool event(bool state); inline bool isActive(void) { return bActive; } inline bool isFull(void) { return (index < max_events); } /* get_index - return the current index This is used by the callers to record where in the event buffer a specific event is stored. (e.g. The start bit of a usart bit stream.) */ inline unsigned int get_index(void) { return index; } bool get_event(int index) { return (index & 1)^bInitialState; } bool get_state(guint64 event_time) { return get_event(get_index(event_time)); } int get_edges(guint64 start_time, guint64 end_time) { return ( get_index(end_time) - get_index(start_time) ); } }; #endif