RubySystem.cc revision 8937
16498Snate@binkert.org/* 26498Snate@binkert.org * Copyright (c) 1999-2011 Mark D. Hill and David A. Wood 36498Snate@binkert.org * All rights reserved. 46498Snate@binkert.org * 56498Snate@binkert.org * Redistribution and use in source and binary forms, with or without 66498Snate@binkert.org * modification, are permitted provided that the following conditions are 76498Snate@binkert.org * met: redistributions of source code must retain the above copyright 86498Snate@binkert.org * notice, this list of conditions and the following disclaimer; 96498Snate@binkert.org * redistributions in binary form must reproduce the above copyright 106498Snate@binkert.org * notice, this list of conditions and the following disclaimer in the 116498Snate@binkert.org * documentation and/or other materials provided with the distribution; 126498Snate@binkert.org * neither the name of the copyright holders nor the names of its 136498Snate@binkert.org * contributors may be used to endorse or promote products derived from 146498Snate@binkert.org * this software without specific prior written permission. 156498Snate@binkert.org * 166498Snate@binkert.org * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 176498Snate@binkert.org * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 186498Snate@binkert.org * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 196498Snate@binkert.org * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 206498Snate@binkert.org * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 216498Snate@binkert.org * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 226498Snate@binkert.org * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 236498Snate@binkert.org * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 246498Snate@binkert.org * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 256498Snate@binkert.org * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 266498Snate@binkert.org * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 276498Snate@binkert.org */ 286498Snate@binkert.org 296498Snate@binkert.org#include <fcntl.h> 306498Snate@binkert.org#include <zlib.h> 316498Snate@binkert.org 326498Snate@binkert.org#include <cstdio> 336498Snate@binkert.org 346498Snate@binkert.org#include "base/intmath.hh" 356498Snate@binkert.org#include "base/output.hh" 366498Snate@binkert.org#include "debug/RubyCacheTrace.hh" 376498Snate@binkert.org#include "mem/ruby/common/Address.hh" 386498Snate@binkert.org#include "mem/ruby/network/Network.hh" 396498Snate@binkert.org#include "mem/ruby/profiler/Profiler.hh" 406498Snate@binkert.org#include "mem/ruby/system/System.hh" 416498Snate@binkert.org#include "sim/eventq.hh" 426498Snate@binkert.org#include "sim/simulate.hh" 436498Snate@binkert.org 446498Snate@binkert.orgusing namespace std; 456498Snate@binkert.org 466498Snate@binkert.orgint RubySystem::m_random_seed; 476498Snate@binkert.orgbool RubySystem::m_randomization; 486498Snate@binkert.orgTick RubySystem::m_clock; 496498Snate@binkert.orgint RubySystem::m_block_size_bytes; 506498Snate@binkert.orgint RubySystem::m_block_size_bits; 516498Snate@binkert.orguint64 RubySystem::m_memory_size_bytes; 526498Snate@binkert.orgint RubySystem::m_memory_size_bits; 536498Snate@binkert.org 546498Snate@binkert.orgNetwork* RubySystem::m_network_ptr; 556498Snate@binkert.orgProfiler* RubySystem::m_profiler_ptr; 566498Snate@binkert.orgMemoryVector* RubySystem::m_mem_vec_ptr; 576498Snate@binkert.org 586498Snate@binkert.orgRubySystem::RubySystem(const Params *p) 596498Snate@binkert.org : SimObject(p) 606498Snate@binkert.org{ 616498Snate@binkert.org if (g_system_ptr != NULL) 626498Snate@binkert.org fatal("Only one RubySystem object currently allowed.\n"); 636498Snate@binkert.org 646498Snate@binkert.org m_random_seed = p->random_seed; 656498Snate@binkert.org srandom(m_random_seed); 666498Snate@binkert.org m_randomization = p->randomization; 676498Snate@binkert.org m_clock = p->clock; 686498Snate@binkert.org 696498Snate@binkert.org m_block_size_bytes = p->block_size_bytes; 706498Snate@binkert.org assert(isPowerOf2(m_block_size_bytes)); 716498Snate@binkert.org m_block_size_bits = floorLog2(m_block_size_bytes); 726498Snate@binkert.org 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 98void 99RubySystem::init() 100{ 101 m_profiler_ptr->clearStats(); 102} 103 104void 105RubySystem::registerNetwork(Network* network_ptr) 106{ 107 m_network_ptr = network_ptr; 108} 109 110void 111RubySystem::registerProfiler(Profiler* profiler_ptr) 112{ 113 m_profiler_ptr = profiler_ptr; 114} 115 116void 117RubySystem::registerAbstractController(AbstractController* cntrl) 118{ 119 m_abs_cntrl_vec.push_back(cntrl); 120} 121 122void 123RubySystem::registerSparseMemory(SparseMemory* s) 124{ 125 m_sparse_memory_vector.push_back(s); 126} 127 128RubySystem::~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 136void 137RubySystem::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 149void 150RubySystem::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 159void 160RubySystem::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 173void 174RubySystem::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 202void 203RubySystem::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 287void 288RubySystem::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 318void 319RubySystem::unserialize(Checkpoint *cp, const string §ion) 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 375void 376RubySystem::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 400void 401RubySystem::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 410void 411RubySystem::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// 426void 427RubySystem::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 469RubySystem * 470RubySystemParams::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 */ 479void 480RubyExitCallback::process() 481{ 482 std::ostream *os = simout.create(stats_filename); 483 RubySystem::printConfig(*os); 484 *os << endl; 485 RubySystem::printStats(*os); 486} 487