1 /*
2    Copyright (C) 1998-2000 T. Scott Dattalo
3 
4 This file is part of the libgpsim library of gpsim
5 
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10 
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 Lesser General Public License for more details.
15 
16 You should have received a copy of the GNU Lesser General Public
17 License along with this library; if not, see
18 <http://www.gnu.org/licenses/lgpl-2.1.html>.
19 */
20 
21 #ifndef SRC_GPSIM_TIME_H_
22 #define SRC_GPSIM_TIME_H_
23 
24 #include "exports.h"
25 #include "breakpoints.h"
26 
27 //---------------------------------------------------------
28 // Cycle Counter
29 //
30 // The cycle counter class is used to coordinate the timing
31 // between the different peripherals within a processor and
32 // in some cases, the timing between several simulated processors
33 // and modules.
34 //
35 // The smallest quantum of simulated time is called a 'cycle'.
36 // The simuluation engine increments a 'Cycle Counter' at quantum
37 // simulation step. Simulation objects that wished to be notified
38 // at a specific instance in time can set a cycle counter break
39 // point that will get invoked whenever the cycle counter reaches
40 // that instance.
41 
42 
43 
44 //------------------------------------------------------------
45 //
46 // Cycle counter breakpoint list
47 //
48 // This is a friend class to the Cycle Counter class. Its purpose
49 // is to maintain a doubly linked list of cycle counter break
50 // points.
51 
52 class Cycle_Counter_breakpoint_list {
53 public:
54   Cycle_Counter_breakpoint_list();
55 
56   Cycle_Counter_breakpoint_list *getNext();
57   Cycle_Counter_breakpoint_list *getPrev();
58   void clear();
59   void invoke();
60 
61   // This is the value compared to the cycle counter.
62   guint64 break_value;
63 
64   // True when this break is active.
65   bool bActive = false;
66 
67   // The breakpoint_number is a number uniquely identifying this
68   // cycle counter break point. Note, this number is used only
69   // when the break point was assigned by a user
70 
71   unsigned int breakpoint_number = 0;
72 
73   // If non-null, the TriggerObject will point to an object that will get invoked
74   // when the breakpoint is encountered.
75 
76   TriggerObject *f = nullptr;
77 
78   // Doubly-linked list mechanics..
79   // (these will be made private eventually)
80   Cycle_Counter_breakpoint_list *next = nullptr;
81   Cycle_Counter_breakpoint_list *prev = nullptr;
82 };
83 
84 
85 class Cycle_Counter {
86 public:
87 #define BREAK_ARRAY_SIZE  4
88 #define BREAK_ARRAY_MASK  (BREAK_ARRAY_SIZE -1)
89 
90   Cycle_Counter();
91   ~Cycle_Counter();
92 
93   void preset(guint64 new_value);     // not used currently.
94 
95   /*
96     increment - This inline member function is called once or
97     twice for every simulated instruction. Its purpose is to
98     increment the cycle counter using roll over arithmetic.
99     If there's a breakpoint set on the new value of the cycle
100     counter then the simulation is either stopped or a callback
101     function is invoked. In either case, the break point is
102     cleared.
103   */
104 
increment()105   inline void increment()
106   {
107     // This has been changed so the cycle counter (value)
108     // is incremented after processing breakpoints
109 
110     // Increment the current cycle then check if
111     // we have a break point set here
112     if (value == break_on_this) {
113       breakpoint();
114     }
115 
116     value++;
117     // Note that it's really inefficient to trace every cycle increment.
118     // Instead, we implicitly trace the increments with the instruction traces.
119   }
120 
121   /*
122     advance the Cycle Counter by more than one instruction quantum.
123     This is almost identical to the increment() function except that
124     we allow the counter to be advanced by an arbitrary amount.
125     They're separated only for efficiency reasons. This one runs slower.
126   */
advance(guint64 step)127   inline void advance(guint64 step)
128   {
129     while (step--) {
130       if (value == break_on_this) {
131         breakpoint();
132       }
133     }
134 
135     value++;
136   }
137 
138   // Return the current cycle counter value
get()139   guint64 get()
140   {
141     return value;
142   }
143 
144   // Return the cycle counter for some time off in the future:
145   guint64 get(double future_time_from_now);
146 
147   bool set_break(guint64 future_cycle,
148                  TriggerObject *f = nullptr, unsigned int abp = MAX_BREAKPOINTS);
149   bool set_break_delta(guint64 future_cycle,
150                        TriggerObject *f = nullptr, unsigned int abp = MAX_BREAKPOINTS);
151   bool reassign_break(guint64 old_cycle, guint64 future_cycle, TriggerObject *f = nullptr);
152   void clear_current_break(TriggerObject *f = nullptr);
153   void dump_breakpoints();
154 
155   void clear_break(guint64 at_cycle);
156   void clear_break(TriggerObject *f);
157   void set_instruction_cps(guint64 cps);
instruction_cps()158   double instruction_cps()
159   {
160     return m_instruction_cps;
161   }
seconds_per_cycle()162   double seconds_per_cycle()
163   {
164     return m_seconds_per_cycle;
165   }
166 
167   // Largest cycle counter value
168 
169   static const guint64  END_OF_TIME = 0xFFFFFFFFFFFFFFFFULL;
170 
171 
172   bool reassigned = false;        // Set true when a break point is reassigned (or deleted)
173 
174   Cycle_Counter_breakpoint_list
175   active,     // Head of the active breakpoint linked list
176   inactive;   // Head of the inactive one.
177 
178   bool bSynchronous = false; // a flag that's true when the time per counter tick is constant
179 
180 private:
181   // The number of instruction cycles that correspond to one second
182   double m_instruction_cps;
183   double m_seconds_per_cycle;
184 
185   guint64 value = 0;          // Current value of the cycle counter.
186   guint64 break_on_this;  // If there's a pending cycle break point, then it'll be this
187 
188   /*
189     breakpoint
190     when the member function "increment()" encounters a break point,
191     breakpoint() is called.
192   */
193 
194   void breakpoint();
195 };
196 
197 
198 #if defined(IN_MODULE) && defined(_WIN32)
199 // we are in a module: don't access cycles object directly!
200 LIBGPSIM_EXPORT Cycle_Counter &get_cycles();
201 #else
202 // we are in gpsim: use of get_cycles() is recommended,
203 // even if cycles object can be accessed directly.
204 extern Cycle_Counter cycles;
205 
get_cycles()206 inline Cycle_Counter &get_cycles()
207 {
208   return cycles;
209 }
210 #endif
211 
212 
213 
214 /// The stopwatch object is used to keep track of the amount of
215 /// time between events. It can be controlled either through the
216 /// class API or through its attributes
217 class StopWatch : public TriggerObject {
218 public:
219   StopWatch();
220   ~StopWatch();
221 
222   guint64 get();
223   double get_time();
224 
225   void set_enable(bool);
226   void set_direction(bool);
227   void set_rollover(guint64);
228   void set_value(guint64);
229 
230   void set_break(bool);
231 
232   void update();
233 
234   virtual void callback();
235   virtual void callback_print();
236 
237 private:
238   Integer *value;
239   Integer *rollover;
240   Boolean *enable;
241   Boolean *direction;
242 
243   bool count_dir;
244 
245   guint64 offset = 0;
246   guint64 break_cycle = 0;
247 };
248 
249 
250 extern StopWatch *stop_watch;
251 
252 
253 #endif
254