Merge zizzer:/bk/m5 into zeep.eecs.umich.edu:/z/saidi/work/m5
[gem5.git] / cpu / base_cpu.cc
1 /*
2 * Copyright (c) 2002-2004 The Regents of The University of Michigan
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met: redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer;
9 * redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution;
12 * neither the name of the copyright holders nor the names of its
13 * contributors may be used to endorse or promote products derived from
14 * this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <string>
30 #include <sstream>
31 #include <iostream>
32
33 #include "base/cprintf.hh"
34 #include "base/loader/symtab.hh"
35 #include "base/misc.hh"
36 #include "cpu/base_cpu.hh"
37 #include "cpu/exec_context.hh"
38 #include "sim/param.hh"
39 #include "sim/sim_events.hh"
40
41 using namespace std;
42
43 vector<BaseCPU *> BaseCPU::cpuList;
44
45 // This variable reflects the max number of threads in any CPU. Be
46 // careful to only use it once all the CPUs that you care about have
47 // been initialized
48 int maxThreadsPerCPU = 1;
49
50 #ifdef FULL_SYSTEM
51 BaseCPU::BaseCPU(const string &_name, int _number_of_threads, bool _def_reg,
52 Counter max_insts_any_thread,
53 Counter max_insts_all_threads,
54 Counter max_loads_any_thread,
55 Counter max_loads_all_threads,
56 System *_system, Tick freq,
57 bool _function_trace, Tick _function_trace_start)
58 : SimObject(_name), frequency(freq), checkInterrupts(true),
59 deferRegistration(_def_reg), number_of_threads(_number_of_threads),
60 system(_system)
61 #else
62 BaseCPU::BaseCPU(const string &_name, int _number_of_threads, bool _def_reg,
63 Counter max_insts_any_thread,
64 Counter max_insts_all_threads,
65 Counter max_loads_any_thread,
66 Counter max_loads_all_threads,
67 bool _function_trace, Tick _function_trace_start)
68 : SimObject(_name), deferRegistration(_def_reg),
69 number_of_threads(_number_of_threads)
70 #endif
71 {
72 // add self to global list of CPUs
73 cpuList.push_back(this);
74
75 if (number_of_threads > maxThreadsPerCPU)
76 maxThreadsPerCPU = number_of_threads;
77
78 // allocate per-thread instruction-based event queues
79 comInstEventQueue = new (EventQueue *)[number_of_threads];
80 for (int i = 0; i < number_of_threads; ++i)
81 comInstEventQueue[i] = new EventQueue("instruction-based event queue");
82
83 //
84 // set up instruction-count-based termination events, if any
85 //
86 if (max_insts_any_thread != 0)
87 for (int i = 0; i < number_of_threads; ++i)
88 new SimExitEvent(comInstEventQueue[i], max_insts_any_thread,
89 "a thread reached the max instruction count");
90
91 if (max_insts_all_threads != 0) {
92 // allocate & initialize shared downcounter: each event will
93 // decrement this when triggered; simulation will terminate
94 // when counter reaches 0
95 int *counter = new int;
96 *counter = number_of_threads;
97 for (int i = 0; i < number_of_threads; ++i)
98 new CountedExitEvent(comInstEventQueue[i],
99 "all threads reached the max instruction count",
100 max_insts_all_threads, *counter);
101 }
102
103 // allocate per-thread load-based event queues
104 comLoadEventQueue = new (EventQueue *)[number_of_threads];
105 for (int i = 0; i < number_of_threads; ++i)
106 comLoadEventQueue[i] = new EventQueue("load-based event queue");
107
108 //
109 // set up instruction-count-based termination events, if any
110 //
111 if (max_loads_any_thread != 0)
112 for (int i = 0; i < number_of_threads; ++i)
113 new SimExitEvent(comLoadEventQueue[i], max_loads_any_thread,
114 "a thread reached the max load count");
115
116 if (max_loads_all_threads != 0) {
117 // allocate & initialize shared downcounter: each event will
118 // decrement this when triggered; simulation will terminate
119 // when counter reaches 0
120 int *counter = new int;
121 *counter = number_of_threads;
122 for (int i = 0; i < number_of_threads; ++i)
123 new CountedExitEvent(comLoadEventQueue[i],
124 "all threads reached the max load count",
125 max_loads_all_threads, *counter);
126 }
127
128 #ifdef FULL_SYSTEM
129 memset(interrupts, 0, sizeof(interrupts));
130 intstatus = 0;
131 #endif
132
133 functionTracingEnabled = false;
134 if (_function_trace) {
135 std::string filename = csprintf("ftrace.%s", name());
136 functionTraceStream = makeOutputStream(filename);
137 currentFunctionStart = currentFunctionEnd = 0;
138 functionEntryTick = _function_trace_start;
139
140 if (_function_trace_start == 0) {
141 functionTracingEnabled = true;
142 } else {
143 Event *e =
144 new EventWrapper<BaseCPU, &BaseCPU::enableFunctionTrace>(this,
145 true);
146 e->schedule(_function_trace_start);
147 }
148 }
149 }
150
151
152 void
153 BaseCPU::enableFunctionTrace()
154 {
155 functionTracingEnabled = true;
156 }
157
158 BaseCPU::~BaseCPU()
159 {
160 if (functionTracingEnabled)
161 closeOutputStream(functionTraceStream);
162 }
163
164
165 void
166 BaseCPU::init()
167 {
168 if (!deferRegistration)
169 registerExecContexts();
170 }
171
172 void
173 BaseCPU::regStats()
174 {
175 using namespace Stats;
176
177 numCycles
178 .name(name() + ".numCycles")
179 .desc("number of cpu cycles simulated")
180 ;
181
182 int size = execContexts.size();
183 if (size > 1) {
184 for (int i = 0; i < size; ++i) {
185 stringstream namestr;
186 ccprintf(namestr, "%s.ctx%d", name(), i);
187 execContexts[i]->regStats(namestr.str());
188 }
189 } else if (size == 1)
190 execContexts[0]->regStats(name());
191 }
192
193
194 void
195 BaseCPU::registerExecContexts()
196 {
197 for (int i = 0; i < execContexts.size(); ++i) {
198 ExecContext *xc = execContexts[i];
199 int cpu_id;
200
201 #ifdef FULL_SYSTEM
202 cpu_id = system->registerExecContext(xc);
203 #else
204 cpu_id = xc->process->registerExecContext(xc);
205 #endif
206
207 xc->cpu_id = cpu_id;
208 }
209 }
210
211
212 void
213 BaseCPU::switchOut()
214 {
215 // default: do nothing
216 }
217
218 void
219 BaseCPU::takeOverFrom(BaseCPU *oldCPU)
220 {
221 assert(execContexts.size() == oldCPU->execContexts.size());
222
223 for (int i = 0; i < execContexts.size(); ++i) {
224 ExecContext *newXC = execContexts[i];
225 ExecContext *oldXC = oldCPU->execContexts[i];
226
227 newXC->takeOverFrom(oldXC);
228 assert(newXC->cpu_id == oldXC->cpu_id);
229 #ifdef FULL_SYSTEM
230 system->replaceExecContext(newXC, newXC->cpu_id);
231 #else
232 assert(newXC->process == oldXC->process);
233 newXC->process->replaceExecContext(newXC, newXC->cpu_id);
234 #endif
235 }
236
237 #ifdef FULL_SYSTEM
238 for (int i = 0; i < NumInterruptLevels; ++i)
239 interrupts[i] = oldCPU->interrupts[i];
240 intstatus = oldCPU->intstatus;
241 #endif
242 }
243
244
245 #ifdef FULL_SYSTEM
246 void
247 BaseCPU::post_interrupt(int int_num, int index)
248 {
249 DPRINTF(Interrupt, "Interrupt %d:%d posted\n", int_num, index);
250
251 if (int_num < 0 || int_num >= NumInterruptLevels)
252 panic("int_num out of bounds\n");
253
254 if (index < 0 || index >= sizeof(uint64_t) * 8)
255 panic("int_num out of bounds\n");
256
257 checkInterrupts = true;
258 interrupts[int_num] |= 1 << index;
259 intstatus |= (ULL(1) << int_num);
260 }
261
262 void
263 BaseCPU::clear_interrupt(int int_num, int index)
264 {
265 DPRINTF(Interrupt, "Interrupt %d:%d cleared\n", int_num, index);
266
267 if (int_num < 0 || int_num >= NumInterruptLevels)
268 panic("int_num out of bounds\n");
269
270 if (index < 0 || index >= sizeof(uint64_t) * 8)
271 panic("int_num out of bounds\n");
272
273 interrupts[int_num] &= ~(1 << index);
274 if (interrupts[int_num] == 0)
275 intstatus &= ~(ULL(1) << int_num);
276 }
277
278 void
279 BaseCPU::clear_interrupts()
280 {
281 DPRINTF(Interrupt, "Interrupts all cleared\n");
282
283 memset(interrupts, 0, sizeof(interrupts));
284 intstatus = 0;
285 }
286
287
288 void
289 BaseCPU::serialize(std::ostream &os)
290 {
291 SERIALIZE_ARRAY(interrupts, NumInterruptLevels);
292 SERIALIZE_SCALAR(intstatus);
293 }
294
295 void
296 BaseCPU::unserialize(Checkpoint *cp, const std::string &section)
297 {
298 UNSERIALIZE_ARRAY(interrupts, NumInterruptLevels);
299 UNSERIALIZE_SCALAR(intstatus);
300 }
301
302 #endif // FULL_SYSTEM
303
304 void
305 BaseCPU::traceFunctionsInternal(Addr pc)
306 {
307 if (!debugSymbolTable)
308 return;
309
310 // if pc enters different function, print new function symbol and
311 // update saved range. Otherwise do nothing.
312 if (pc < currentFunctionStart || pc >= currentFunctionEnd) {
313 string sym_str;
314 bool found = debugSymbolTable->findNearestSymbol(pc, sym_str,
315 currentFunctionStart,
316 currentFunctionEnd);
317
318 if (!found) {
319 // no symbol found: use addr as label
320 sym_str = csprintf("0x%x", pc);
321 currentFunctionStart = pc;
322 currentFunctionEnd = pc + 1;
323 }
324
325 ccprintf(*functionTraceStream, " (%d)\n%d: %s",
326 curTick - functionEntryTick, curTick, sym_str);
327 functionEntryTick = curTick;
328 }
329 }
330
331
332 DEFINE_SIM_OBJECT_CLASS_NAME("BaseCPU", BaseCPU)