sim-ruby: checkpointing fixes and dependent eventq improvements
[gem5.git] / src / mem / ruby / system / System.cc
1 /*
2 * Copyright (c) 1999-2011 Mark D. Hill and David A. Wood
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 <fcntl.h>
30 #include <zlib.h>
31
32 #include <cstdio>
33
34 #include "base/intmath.hh"
35 #include "base/output.hh"
36 #include "debug/RubyCacheTrace.hh"
37 #include "mem/ruby/common/Address.hh"
38 #include "mem/ruby/network/Network.hh"
39 #include "mem/ruby/profiler/Profiler.hh"
40 #include "mem/ruby/system/System.hh"
41 #include "sim/eventq.hh"
42 #include "sim/simulate.hh"
43
44 using namespace std;
45
46 int RubySystem::m_random_seed;
47 bool RubySystem::m_randomization;
48 Tick RubySystem::m_clock;
49 int RubySystem::m_block_size_bytes;
50 int RubySystem::m_block_size_bits;
51 uint64 RubySystem::m_memory_size_bytes;
52 int RubySystem::m_memory_size_bits;
53
54 Network* RubySystem::m_network_ptr;
55 Profiler* RubySystem::m_profiler_ptr;
56 MemoryVector* RubySystem::m_mem_vec_ptr;
57
58 RubySystem::RubySystem(const Params *p)
59 : SimObject(p)
60 {
61 if (g_system_ptr != NULL)
62 fatal("Only one RubySystem object currently allowed.\n");
63
64 m_random_seed = p->random_seed;
65 srandom(m_random_seed);
66 m_randomization = p->randomization;
67 m_clock = p->clock;
68
69 m_block_size_bytes = p->block_size_bytes;
70 assert(isPowerOf2(m_block_size_bytes));
71 m_block_size_bits = floorLog2(m_block_size_bytes);
72
73 m_memory_size_bytes = p->mem_size;
74 if (m_memory_size_bytes == 0) {
75 m_memory_size_bits = 0;
76 } else {
77 m_memory_size_bits = floorLog2(m_memory_size_bytes);
78 }
79
80 g_eventQueue_ptr = new RubyEventQueue(p->eventq, m_clock);
81 g_system_ptr = this;
82 if (p->no_mem_vec) {
83 m_mem_vec_ptr = NULL;
84 } else {
85 m_mem_vec_ptr = new MemoryVector;
86 m_mem_vec_ptr->resize(m_memory_size_bytes);
87 }
88
89 //
90 // Print ruby configuration and stats at exit
91 //
92 RubyExitCallback* rubyExitCB = new RubyExitCallback(p->stats_filename);
93 registerExitCallback(rubyExitCB);
94 m_warmup_enabled = false;
95 m_cooldown_enabled = false;
96 }
97
98 void
99 RubySystem::init()
100 {
101 m_profiler_ptr->clearStats();
102 }
103
104 void
105 RubySystem::registerNetwork(Network* network_ptr)
106 {
107 m_network_ptr = network_ptr;
108 }
109
110 void
111 RubySystem::registerProfiler(Profiler* profiler_ptr)
112 {
113 m_profiler_ptr = profiler_ptr;
114 }
115
116 void
117 RubySystem::registerAbstractController(AbstractController* cntrl)
118 {
119 m_abs_cntrl_vec.push_back(cntrl);
120 }
121
122 void
123 RubySystem::registerSparseMemory(SparseMemory* s)
124 {
125 m_sparse_memory_vector.push_back(s);
126 }
127
128 RubySystem::~RubySystem()
129 {
130 delete m_network_ptr;
131 delete m_profiler_ptr;
132 if (m_mem_vec_ptr)
133 delete m_mem_vec_ptr;
134 }
135
136 void
137 RubySystem::printSystemConfig(ostream & out)
138 {
139 out << "RubySystem config:" << endl
140 << " random_seed: " << m_random_seed << endl
141 << " randomization: " << m_randomization << endl
142 << " cycle_period: " << m_clock << endl
143 << " block_size_bytes: " << m_block_size_bytes << endl
144 << " block_size_bits: " << m_block_size_bits << endl
145 << " memory_size_bytes: " << m_memory_size_bytes << endl
146 << " memory_size_bits: " << m_memory_size_bits << endl;
147 }
148
149 void
150 RubySystem::printConfig(ostream& out)
151 {
152 out << "\n================ Begin RubySystem Configuration Print ================\n\n";
153 printSystemConfig(out);
154 m_network_ptr->printConfig(out);
155 m_profiler_ptr->printConfig(out);
156 out << "\n================ End RubySystem Configuration Print ================\n\n";
157 }
158
159 void
160 RubySystem::printStats(ostream& out)
161 {
162 const time_t T = time(NULL);
163 tm *localTime = localtime(&T);
164 char buf[100];
165 strftime(buf, 100, "%b/%d/%Y %H:%M:%S", localTime);
166
167 out << "Real time: " << buf << endl;
168
169 m_profiler_ptr->printStats(out);
170 m_network_ptr->printStats(out);
171 }
172
173 void
174 RubySystem::writeCompressedTrace(uint8* raw_data, string filename,
175 uint64 uncompressed_trace_size)
176 {
177 // Create the checkpoint file for the memory
178 string thefile = Checkpoint::dir() + "/" + filename.c_str();
179
180 int fd = creat(thefile.c_str(), 0664);
181 if (fd < 0) {
182 perror("creat");
183 fatal("Can't open memory trace file '%s'\n", filename);
184 }
185
186 gzFile compressedMemory = gzdopen(fd, "wb");
187 if (compressedMemory == NULL)
188 fatal("Insufficient memory to allocate compression state for %s\n",
189 filename);
190
191 if (gzwrite(compressedMemory, raw_data, uncompressed_trace_size) !=
192 uncompressed_trace_size) {
193 fatal("Write failed on memory trace file '%s'\n", filename);
194 }
195
196 if (gzclose(compressedMemory)) {
197 fatal("Close failed on memory trace file '%s'\n", filename);
198 }
199 delete raw_data;
200 }
201
202 void
203 RubySystem::serialize(std::ostream &os)
204 {
205 m_cooldown_enabled = true;
206
207 vector<Sequencer*> sequencer_map;
208 Sequencer* sequencer_ptr = NULL;
209 int cntrl_id = -1;
210
211
212 for (int cntrl = 0; cntrl < m_abs_cntrl_vec.size(); cntrl++) {
213 sequencer_map.push_back(m_abs_cntrl_vec[cntrl]->getSequencer());
214 if (sequencer_ptr == NULL) {
215 sequencer_ptr = sequencer_map[cntrl];
216 cntrl_id = cntrl;
217 }
218 }
219
220 assert(sequencer_ptr != NULL);
221
222 for (int cntrl = 0; cntrl < m_abs_cntrl_vec.size(); cntrl++) {
223 if (sequencer_map[cntrl] == NULL) {
224 sequencer_map[cntrl] = sequencer_ptr;
225 }
226 }
227
228 DPRINTF(RubyCacheTrace, "Recording Cache Trace\n");
229 // Create the CacheRecorder and record the cache trace
230 m_cache_recorder = new CacheRecorder(NULL, 0, sequencer_map);
231
232 for (int cntrl = 0; cntrl < m_abs_cntrl_vec.size(); cntrl++) {
233 m_abs_cntrl_vec[cntrl]->recordCacheTrace(cntrl, m_cache_recorder);
234 }
235
236 DPRINTF(RubyCacheTrace, "Cache Trace Complete\n");
237 // save the current tick value
238 Tick curtick_original = curTick();
239 // save the event queue head
240 Event* eventq_head = eventq->replaceHead(NULL);
241 DPRINTF(RubyCacheTrace, "Recording current tick %ld and event queue\n",
242 curtick_original);
243
244 // Schedule an event to start cache cooldown
245 DPRINTF(RubyCacheTrace, "Starting cache flush\n");
246 enqueueRubyEvent(curTick());
247 simulate();
248 DPRINTF(RubyCacheTrace, "Cache flush complete\n");
249
250 // Restore eventq head
251 eventq_head = eventq->replaceHead(eventq_head);
252 // Restore curTick
253 curTick(curtick_original);
254
255 uint8* raw_data = NULL;
256
257 if (m_mem_vec_ptr != NULL) {
258 uint64 memory_trace_size = m_mem_vec_ptr->collatePages(raw_data);
259
260 string memory_trace_file = name() + ".memory.gz";
261 writeCompressedTrace(raw_data, memory_trace_file,
262 memory_trace_size);
263
264 SERIALIZE_SCALAR(memory_trace_file);
265 SERIALIZE_SCALAR(memory_trace_size);
266
267 } else {
268 for (int i = 0; i < m_sparse_memory_vector.size(); ++i) {
269 m_sparse_memory_vector[i]->recordBlocks(cntrl_id,
270 m_cache_recorder);
271 }
272 }
273
274 // Aggergate the trace entries together into a single array
275 raw_data = new uint8_t[4096];
276 uint64 cache_trace_size = m_cache_recorder->aggregateRecords(&raw_data,
277 4096);
278 string cache_trace_file = name() + ".cache.gz";
279 writeCompressedTrace(raw_data, cache_trace_file, cache_trace_size);
280
281 SERIALIZE_SCALAR(cache_trace_file);
282 SERIALIZE_SCALAR(cache_trace_size);
283
284 m_cooldown_enabled = false;
285 }
286
287 void
288 RubySystem::readCompressedTrace(string filename, uint8*& raw_data,
289 uint64& uncompressed_trace_size)
290 {
291 // Read the trace file
292 gzFile compressedTrace;
293
294 // trace file
295 int fd = open(filename.c_str(), O_RDONLY);
296 if (fd < 0) {
297 perror("open");
298 fatal("Unable to open trace file %s", filename);
299 }
300
301 compressedTrace = gzdopen(fd, "rb");
302 if (compressedTrace == NULL) {
303 fatal("Insufficient memory to allocate compression state for %s\n",
304 filename);
305 }
306
307 raw_data = new uint8_t[uncompressed_trace_size];
308 if (gzread(compressedTrace, raw_data, uncompressed_trace_size) <
309 uncompressed_trace_size) {
310 fatal("Unable to read complete trace from file %s\n", filename);
311 }
312
313 if (gzclose(compressedTrace)) {
314 fatal("Failed to close cache trace file '%s'\n", filename);
315 }
316 }
317
318 void
319 RubySystem::unserialize(Checkpoint *cp, const string &section)
320 {
321 //
322 // The main purpose for clearing stats in the unserialize process is so
323 // that the profiler can correctly set its start time to the unserialized
324 // value of curTick()
325 //
326 clearStats();
327 uint8* uncompressed_trace = NULL;
328
329 if (m_mem_vec_ptr != NULL) {
330 string memory_trace_file;
331 uint64 memory_trace_size = 0;
332
333 UNSERIALIZE_SCALAR(memory_trace_file);
334 UNSERIALIZE_SCALAR(memory_trace_size);
335 memory_trace_file = cp->cptDir + "/" + memory_trace_file;
336
337 readCompressedTrace(memory_trace_file, uncompressed_trace,
338 memory_trace_size);
339 m_mem_vec_ptr->populatePages(uncompressed_trace);
340
341 delete uncompressed_trace;
342 uncompressed_trace = NULL;
343 }
344
345 string cache_trace_file;
346 uint64 cache_trace_size = 0;
347
348 UNSERIALIZE_SCALAR(cache_trace_file);
349 UNSERIALIZE_SCALAR(cache_trace_size);
350 cache_trace_file = cp->cptDir + "/" + cache_trace_file;
351
352 readCompressedTrace(cache_trace_file, uncompressed_trace,
353 cache_trace_size);
354 m_warmup_enabled = true;
355
356 vector<Sequencer*> sequencer_map;
357 Sequencer* t = NULL;
358 for (int cntrl = 0; cntrl < m_abs_cntrl_vec.size(); cntrl++) {
359 sequencer_map.push_back(m_abs_cntrl_vec[cntrl]->getSequencer());
360 if(t == NULL) t = sequencer_map[cntrl];
361 }
362
363 assert(t != NULL);
364
365 for (int cntrl = 0; cntrl < m_abs_cntrl_vec.size(); cntrl++) {
366 if (sequencer_map[cntrl] == NULL) {
367 sequencer_map[cntrl] = t;
368 }
369 }
370
371 m_cache_recorder = new CacheRecorder(uncompressed_trace, cache_trace_size,
372 sequencer_map);
373 }
374
375 void
376 RubySystem::startup()
377 {
378 if (m_warmup_enabled) {
379 // save the current tick value
380 Tick curtick_original = curTick();
381 // save the event queue head
382 Event* eventq_head = eventq->replaceHead(NULL);
383 // set curTick to 0
384 curTick(0);
385
386 // Schedule an event to start cache warmup
387 enqueueRubyEvent(curTick());
388 simulate();
389
390 delete m_cache_recorder;
391 m_cache_recorder = NULL;
392 m_warmup_enabled = false;
393 // Restore eventq head
394 eventq_head = eventq->replaceHead(eventq_head);
395 // Restore curTick
396 curTick(curtick_original);
397 }
398 }
399
400 void
401 RubySystem::RubyEvent::process()
402 {
403 if (ruby_system->m_warmup_enabled) {
404 ruby_system->m_cache_recorder->enqueueNextFetchRequest();
405 } else if (ruby_system->m_cooldown_enabled) {
406 ruby_system->m_cache_recorder->enqueueNextFlushRequest();
407 }
408 }
409
410 void
411 RubySystem::clearStats() const
412 {
413 m_profiler_ptr->clearStats();
414 m_network_ptr->clearStats();
415 }
416
417 #ifdef CHECK_COHERENCE
418 // This code will check for cases if the given cache block is exclusive in
419 // one node and shared in another-- a coherence violation
420 //
421 // To use, the SLICC specification must call sequencer.checkCoherence(address)
422 // when the controller changes to a state with new permissions. Do this
423 // in setState. The SLICC spec must also define methods "isBlockShared"
424 // and "isBlockExclusive" that are specific to that protocol
425 //
426 void
427 RubySystem::checkGlobalCoherenceInvariant(const Address& addr)
428 {
429 #if 0
430 NodeID exclusive = -1;
431 bool sharedDetected = false;
432 NodeID lastShared = -1;
433
434 for (int i = 0; i < m_chip_vector.size(); i++) {
435 if (m_chip_vector[i]->isBlockExclusive(addr)) {
436 if (exclusive != -1) {
437 // coherence violation
438 WARN_EXPR(exclusive);
439 WARN_EXPR(m_chip_vector[i]->getID());
440 WARN_EXPR(addr);
441 WARN_EXPR(g_eventQueue_ptr->getTime());
442 ERROR_MSG("Coherence Violation Detected -- 2 exclusive chips");
443 } else if (sharedDetected) {
444 WARN_EXPR(lastShared);
445 WARN_EXPR(m_chip_vector[i]->getID());
446 WARN_EXPR(addr);
447 WARN_EXPR(g_eventQueue_ptr->getTime());
448 ERROR_MSG("Coherence Violation Detected -- exclusive chip with >=1 shared");
449 } else {
450 exclusive = m_chip_vector[i]->getID();
451 }
452 } else if (m_chip_vector[i]->isBlockShared(addr)) {
453 sharedDetected = true;
454 lastShared = m_chip_vector[i]->getID();
455
456 if (exclusive != -1) {
457 WARN_EXPR(lastShared);
458 WARN_EXPR(exclusive);
459 WARN_EXPR(addr);
460 WARN_EXPR(g_eventQueue_ptr->getTime());
461 ERROR_MSG("Coherence Violation Detected -- exclusive chip with >=1 shared");
462 }
463 }
464 }
465 #endif
466 }
467 #endif
468
469 RubySystem *
470 RubySystemParams::create()
471 {
472 return new RubySystem(this);
473 }
474
475 /**
476 * virtual process function that is invoked when the callback
477 * queue is executed.
478 */
479 void
480 RubyExitCallback::process()
481 {
482 std::ostream *os = simout.create(stats_filename);
483 RubySystem::printConfig(*os);
484 *os << endl;
485 RubySystem::printStats(*os);
486 }