RubySystem.cc revision 10163
15086Sgblack@eecs.umich.edu/* 25086Sgblack@eecs.umich.edu * Copyright (c) 1999-2011 Mark D. Hill and David A. Wood 35086Sgblack@eecs.umich.edu * All rights reserved. 45086Sgblack@eecs.umich.edu * 57087Snate@binkert.org * Redistribution and use in source and binary forms, with or without 67087Snate@binkert.org * modification, are permitted provided that the following conditions are 77087Snate@binkert.org * met: redistributions of source code must retain the above copyright 87087Snate@binkert.org * notice, this list of conditions and the following disclaimer; 97087Snate@binkert.org * redistributions in binary form must reproduce the above copyright 107087Snate@binkert.org * notice, this list of conditions and the following disclaimer in the 117087Snate@binkert.org * documentation and/or other materials provided with the distribution; 127087Snate@binkert.org * neither the name of the copyright holders nor the names of its 135086Sgblack@eecs.umich.edu * contributors may be used to endorse or promote products derived from 147087Snate@binkert.org * this software without specific prior written permission. 157087Snate@binkert.org * 167087Snate@binkert.org * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 177087Snate@binkert.org * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 187087Snate@binkert.org * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 197087Snate@binkert.org * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 207087Snate@binkert.org * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 217087Snate@binkert.org * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 225086Sgblack@eecs.umich.edu * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 237087Snate@binkert.org * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 245086Sgblack@eecs.umich.edu * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 255086Sgblack@eecs.umich.edu * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 265086Sgblack@eecs.umich.edu * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 275086Sgblack@eecs.umich.edu */ 285086Sgblack@eecs.umich.edu 295086Sgblack@eecs.umich.edu#include <fcntl.h> 305086Sgblack@eecs.umich.edu#include <zlib.h> 315086Sgblack@eecs.umich.edu 325086Sgblack@eecs.umich.edu#include <cstdio> 335086Sgblack@eecs.umich.edu 345086Sgblack@eecs.umich.edu#include "base/intmath.hh" 355086Sgblack@eecs.umich.edu#include "base/statistics.hh" 365086Sgblack@eecs.umich.edu#include "debug/RubyCacheTrace.hh" 375086Sgblack@eecs.umich.edu#include "debug/RubySystem.hh" 385086Sgblack@eecs.umich.edu#include "mem/ruby/common/Address.hh" 395086Sgblack@eecs.umich.edu#include "mem/ruby/network/Network.hh" 405647Sgblack@eecs.umich.edu#include "mem/ruby/system/System.hh" 415647Sgblack@eecs.umich.edu#include "sim/eventq.hh" 425647Sgblack@eecs.umich.edu#include "sim/simulate.hh" 435647Sgblack@eecs.umich.edu 445647Sgblack@eecs.umich.eduusing namespace std; 455135Sgblack@eecs.umich.edu 465135Sgblack@eecs.umich.eduint RubySystem::m_random_seed; 475135Sgblack@eecs.umich.edubool RubySystem::m_randomization; 485086Sgblack@eecs.umich.eduuint32_t RubySystem::m_block_size_bytes; 495135Sgblack@eecs.umich.eduuint32_t RubySystem::m_block_size_bits; 505647Sgblack@eecs.umich.eduuint64_t RubySystem::m_memory_size_bytes; 515234Sgblack@eecs.umich.eduuint32_t RubySystem::m_memory_size_bits; 525086Sgblack@eecs.umich.edu 535086Sgblack@eecs.umich.eduRubySystem::RubySystem(const Params *p) 545086Sgblack@eecs.umich.edu : ClockedObject(p) 555086Sgblack@eecs.umich.edu{ 565086Sgblack@eecs.umich.edu if (g_system_ptr != NULL) 575086Sgblack@eecs.umich.edu fatal("Only one RubySystem object currently allowed.\n"); 585086Sgblack@eecs.umich.edu 595086Sgblack@eecs.umich.edu m_random_seed = p->random_seed; 605086Sgblack@eecs.umich.edu srandom(m_random_seed); 615086Sgblack@eecs.umich.edu m_randomization = p->randomization; 625086Sgblack@eecs.umich.edu 635135Sgblack@eecs.umich.edu m_block_size_bytes = p->block_size_bytes; 645135Sgblack@eecs.umich.edu assert(isPowerOf2(m_block_size_bytes)); 655135Sgblack@eecs.umich.edu m_block_size_bits = floorLog2(m_block_size_bytes); 665135Sgblack@eecs.umich.edu 676048Sgblack@eecs.umich.edu m_memory_size_bytes = p->mem_size; 686048Sgblack@eecs.umich.edu if (m_memory_size_bytes == 0) { 696048Sgblack@eecs.umich.edu m_memory_size_bits = 0; 706048Sgblack@eecs.umich.edu } else { 716048Sgblack@eecs.umich.edu m_memory_size_bits = ceilLog2(m_memory_size_bytes); 726048Sgblack@eecs.umich.edu } 736048Sgblack@eecs.umich.edu 746048Sgblack@eecs.umich.edu if (p->no_mem_vec) { 755135Sgblack@eecs.umich.edu m_mem_vec = NULL; 765135Sgblack@eecs.umich.edu } else { 775135Sgblack@eecs.umich.edu m_mem_vec = new MemoryVector; 785135Sgblack@eecs.umich.edu m_mem_vec->resize(m_memory_size_bytes); 795135Sgblack@eecs.umich.edu } 805135Sgblack@eecs.umich.edu 815135Sgblack@eecs.umich.edu m_warmup_enabled = false; 825135Sgblack@eecs.umich.edu m_cooldown_enabled = false; 835135Sgblack@eecs.umich.edu 845135Sgblack@eecs.umich.edu // Setup the global variables used in Ruby 855135Sgblack@eecs.umich.edu g_system_ptr = this; 865135Sgblack@eecs.umich.edu 875135Sgblack@eecs.umich.edu // Resize to the size of different machine types 885135Sgblack@eecs.umich.edu g_abs_controls.resize(MachineType_NUM); 895135Sgblack@eecs.umich.edu 905135Sgblack@eecs.umich.edu // Collate the statistics before they are printed. 915135Sgblack@eecs.umich.edu Stats::registerDumpCallback(new RubyStatsCallback(this)); 925264Sgblack@eecs.umich.edu // Create the profiler 935135Sgblack@eecs.umich.edu m_profiler = new Profiler(p); 945135Sgblack@eecs.umich.edu} 955135Sgblack@eecs.umich.edu 965135Sgblack@eecs.umich.eduvoid 975141Sgblack@eecs.umich.eduRubySystem::registerNetwork(Network* network_ptr) 985141Sgblack@eecs.umich.edu{ 995141Sgblack@eecs.umich.edu m_network = network_ptr; 1005141Sgblack@eecs.umich.edu} 1015141Sgblack@eecs.umich.edu 1025141Sgblack@eecs.umich.eduvoid 1035141Sgblack@eecs.umich.eduRubySystem::registerAbstractController(AbstractController* cntrl) 1045141Sgblack@eecs.umich.edu{ 1055141Sgblack@eecs.umich.edu m_abs_cntrl_vec.push_back(cntrl); 1065182Sgblack@eecs.umich.edu 1075141Sgblack@eecs.umich.edu MachineID id = cntrl->getMachineID(); 1085141Sgblack@eecs.umich.edu g_abs_controls[id.getType()][id.getNum()] = cntrl; 1095141Sgblack@eecs.umich.edu} 1105141Sgblack@eecs.umich.edu 1115141Sgblack@eecs.umich.eduvoid 1125141Sgblack@eecs.umich.eduRubySystem::registerSparseMemory(SparseMemory* s) 1135135Sgblack@eecs.umich.edu{ 1145141Sgblack@eecs.umich.edu m_sparse_memory_vector.push_back(s); 1155141Sgblack@eecs.umich.edu} 1165141Sgblack@eecs.umich.edu 1175141Sgblack@eecs.umich.eduvoid 1185141Sgblack@eecs.umich.eduRubySystem::registerMemController(MemoryControl *mc) { 1195141Sgblack@eecs.umich.edu m_memory_controller_vec.push_back(mc); 1205141Sgblack@eecs.umich.edu} 1215141Sgblack@eecs.umich.edu 1225141Sgblack@eecs.umich.eduRubySystem::~RubySystem() 1235141Sgblack@eecs.umich.edu{ 1245141Sgblack@eecs.umich.edu delete m_network; 1255141Sgblack@eecs.umich.edu delete m_profiler; 1265135Sgblack@eecs.umich.edu if (m_mem_vec) 1275141Sgblack@eecs.umich.edu delete m_mem_vec; 1285141Sgblack@eecs.umich.edu} 1295135Sgblack@eecs.umich.edu 1305141Sgblack@eecs.umich.eduvoid 1315141Sgblack@eecs.umich.eduRubySystem::writeCompressedTrace(uint8_t *raw_data, string filename, 1325141Sgblack@eecs.umich.edu uint64 uncompressed_trace_size) 1335141Sgblack@eecs.umich.edu{ 1345135Sgblack@eecs.umich.edu // Create the checkpoint file for the memory 1355141Sgblack@eecs.umich.edu string thefile = Checkpoint::dir() + "/" + filename.c_str(); 1365141Sgblack@eecs.umich.edu 1375141Sgblack@eecs.umich.edu int fd = creat(thefile.c_str(), 0664); 1385141Sgblack@eecs.umich.edu if (fd < 0) { 1395141Sgblack@eecs.umich.edu perror("creat"); 1405141Sgblack@eecs.umich.edu fatal("Can't open memory trace file '%s'\n", filename); 1415141Sgblack@eecs.umich.edu } 1425141Sgblack@eecs.umich.edu 1435141Sgblack@eecs.umich.edu gzFile compressedMemory = gzdopen(fd, "wb"); 1445141Sgblack@eecs.umich.edu if (compressedMemory == NULL) 1455141Sgblack@eecs.umich.edu fatal("Insufficient memory to allocate compression state for %s\n", 1465141Sgblack@eecs.umich.edu filename); 1475264Sgblack@eecs.umich.edu 1485141Sgblack@eecs.umich.edu if (gzwrite(compressedMemory, raw_data, uncompressed_trace_size) != 1495141Sgblack@eecs.umich.edu uncompressed_trace_size) { 1505141Sgblack@eecs.umich.edu fatal("Write failed on memory trace file '%s'\n", filename); 1515141Sgblack@eecs.umich.edu } 1525141Sgblack@eecs.umich.edu 1535141Sgblack@eecs.umich.edu if (gzclose(compressedMemory)) { 1545141Sgblack@eecs.umich.edu fatal("Close failed on memory trace file '%s'\n", filename); 1555141Sgblack@eecs.umich.edu } 1565141Sgblack@eecs.umich.edu delete raw_data; 1575141Sgblack@eecs.umich.edu} 1585141Sgblack@eecs.umich.edu 1595141Sgblack@eecs.umich.eduvoid 1605141Sgblack@eecs.umich.eduRubySystem::serialize(std::ostream &os) 1615141Sgblack@eecs.umich.edu{ 1625141Sgblack@eecs.umich.edu m_cooldown_enabled = true; 1635141Sgblack@eecs.umich.edu 1645141Sgblack@eecs.umich.edu vector<Sequencer*> sequencer_map; 1655135Sgblack@eecs.umich.edu Sequencer* sequencer_ptr = NULL; 1665135Sgblack@eecs.umich.edu int cntrl_id = -1; 1675135Sgblack@eecs.umich.edu 1685360Sgblack@eecs.umich.edu 1695360Sgblack@eecs.umich.edu for (int cntrl = 0; cntrl < m_abs_cntrl_vec.size(); cntrl++) { 1705360Sgblack@eecs.umich.edu sequencer_map.push_back(m_abs_cntrl_vec[cntrl]->getSequencer()); 1715360Sgblack@eecs.umich.edu if (sequencer_ptr == NULL) { 1725360Sgblack@eecs.umich.edu sequencer_ptr = sequencer_map[cntrl]; 1735360Sgblack@eecs.umich.edu cntrl_id = cntrl; 1745647Sgblack@eecs.umich.edu } 1755647Sgblack@eecs.umich.edu } 1765647Sgblack@eecs.umich.edu 1775360Sgblack@eecs.umich.edu assert(sequencer_ptr != NULL); 1785647Sgblack@eecs.umich.edu 1795647Sgblack@eecs.umich.edu for (int cntrl = 0; cntrl < m_abs_cntrl_vec.size(); cntrl++) { 1805647Sgblack@eecs.umich.edu if (sequencer_map[cntrl] == NULL) { 1815648Sgblack@eecs.umich.edu sequencer_map[cntrl] = sequencer_ptr; 1825648Sgblack@eecs.umich.edu } 1835360Sgblack@eecs.umich.edu } 1845141Sgblack@eecs.umich.edu 1855141Sgblack@eecs.umich.edu // Store the cache-block size, so we are able to restore on systems with a 1865141Sgblack@eecs.umich.edu // different cache-block size. CacheRecorder depends on the correct 1875141Sgblack@eecs.umich.edu // cache-block size upon unserializing. 1885141Sgblack@eecs.umich.edu uint64 block_size_bytes = getBlockSizeBytes(); 1895141Sgblack@eecs.umich.edu SERIALIZE_SCALAR(block_size_bytes); 1905135Sgblack@eecs.umich.edu 1915135Sgblack@eecs.umich.edu DPRINTF(RubyCacheTrace, "Recording Cache Trace\n"); 1925135Sgblack@eecs.umich.edu // Create the CacheRecorder and record the cache trace 1935135Sgblack@eecs.umich.edu m_cache_recorder = new CacheRecorder(NULL, 0, sequencer_map, 1945135Sgblack@eecs.umich.edu block_size_bytes); 1955135Sgblack@eecs.umich.edu 1966042Sgblack@eecs.umich.edu for (int cntrl = 0; cntrl < m_abs_cntrl_vec.size(); cntrl++) { 1975135Sgblack@eecs.umich.edu m_abs_cntrl_vec[cntrl]->recordCacheTrace(cntrl, m_cache_recorder); 1985135Sgblack@eecs.umich.edu } 1995135Sgblack@eecs.umich.edu 2005135Sgblack@eecs.umich.edu DPRINTF(RubyCacheTrace, "Cache Trace Complete\n"); 2015135Sgblack@eecs.umich.edu // save the current tick value 2025135Sgblack@eecs.umich.edu Tick curtick_original = curTick(); 2036042Sgblack@eecs.umich.edu // save the event queue head 2045135Sgblack@eecs.umich.edu Event* eventq_head = eventq->replaceHead(NULL); 2056042Sgblack@eecs.umich.edu DPRINTF(RubyCacheTrace, "Recording current tick %ld and event queue\n", 2066042Sgblack@eecs.umich.edu curtick_original); 2076042Sgblack@eecs.umich.edu 2085135Sgblack@eecs.umich.edu // Schedule an event to start cache cooldown 2095135Sgblack@eecs.umich.edu DPRINTF(RubyCacheTrace, "Starting cache flush\n"); 2106329Sgblack@eecs.umich.edu enqueueRubyEvent(curTick()); 2116329Sgblack@eecs.umich.edu simulate(); 2126329Sgblack@eecs.umich.edu DPRINTF(RubyCacheTrace, "Cache flush complete\n"); 2136329Sgblack@eecs.umich.edu 2146329Sgblack@eecs.umich.edu // Restore eventq head 2156329Sgblack@eecs.umich.edu eventq_head = eventq->replaceHead(eventq_head); 2166329Sgblack@eecs.umich.edu // Restore curTick 2176329Sgblack@eecs.umich.edu setCurTick(curtick_original); 2186329Sgblack@eecs.umich.edu 2196329Sgblack@eecs.umich.edu uint8_t *raw_data = NULL; 2206329Sgblack@eecs.umich.edu 2216329Sgblack@eecs.umich.edu if (m_mem_vec != NULL) { 2226329Sgblack@eecs.umich.edu uint64 memory_trace_size = m_mem_vec->collatePages(raw_data); 2236329Sgblack@eecs.umich.edu 2246329Sgblack@eecs.umich.edu string memory_trace_file = name() + ".memory.gz"; 2256329Sgblack@eecs.umich.edu writeCompressedTrace(raw_data, memory_trace_file, 2266329Sgblack@eecs.umich.edu memory_trace_size); 2276329Sgblack@eecs.umich.edu 2286329Sgblack@eecs.umich.edu SERIALIZE_SCALAR(memory_trace_file); 2296329Sgblack@eecs.umich.edu SERIALIZE_SCALAR(memory_trace_size); 2306329Sgblack@eecs.umich.edu 2316329Sgblack@eecs.umich.edu } else { 2326329Sgblack@eecs.umich.edu for (int i = 0; i < m_sparse_memory_vector.size(); ++i) { 2336329Sgblack@eecs.umich.edu m_sparse_memory_vector[i]->recordBlocks(cntrl_id, 2346329Sgblack@eecs.umich.edu m_cache_recorder); 2356329Sgblack@eecs.umich.edu } 2365086Sgblack@eecs.umich.edu } 237 238 // Aggergate the trace entries together into a single array 239 raw_data = new uint8_t[4096]; 240 uint64 cache_trace_size = m_cache_recorder->aggregateRecords(&raw_data, 241 4096); 242 string cache_trace_file = name() + ".cache.gz"; 243 writeCompressedTrace(raw_data, cache_trace_file, cache_trace_size); 244 245 SERIALIZE_SCALAR(cache_trace_file); 246 SERIALIZE_SCALAR(cache_trace_size); 247 248 m_cooldown_enabled = false; 249} 250 251void 252RubySystem::readCompressedTrace(string filename, uint8_t *&raw_data, 253 uint64& uncompressed_trace_size) 254{ 255 // Read the trace file 256 gzFile compressedTrace; 257 258 // trace file 259 int fd = open(filename.c_str(), O_RDONLY); 260 if (fd < 0) { 261 perror("open"); 262 fatal("Unable to open trace file %s", filename); 263 } 264 265 compressedTrace = gzdopen(fd, "rb"); 266 if (compressedTrace == NULL) { 267 fatal("Insufficient memory to allocate compression state for %s\n", 268 filename); 269 } 270 271 raw_data = new uint8_t[uncompressed_trace_size]; 272 if (gzread(compressedTrace, raw_data, uncompressed_trace_size) < 273 uncompressed_trace_size) { 274 fatal("Unable to read complete trace from file %s\n", filename); 275 } 276 277 if (gzclose(compressedTrace)) { 278 fatal("Failed to close cache trace file '%s'\n", filename); 279 } 280} 281 282void 283RubySystem::unserialize(Checkpoint *cp, const string §ion) 284{ 285 uint8_t *uncompressed_trace = NULL; 286 287 // This value should be set to the checkpoint-system's block-size. 288 // Optional, as checkpoints without it can be run if the 289 // checkpoint-system's block-size == current block-size. 290 uint64 block_size_bytes = getBlockSizeBytes(); 291 UNSERIALIZE_OPT_SCALAR(block_size_bytes); 292 293 if (m_mem_vec != NULL) { 294 string memory_trace_file; 295 uint64 memory_trace_size = 0; 296 297 UNSERIALIZE_SCALAR(memory_trace_file); 298 UNSERIALIZE_SCALAR(memory_trace_size); 299 memory_trace_file = cp->cptDir + "/" + memory_trace_file; 300 301 readCompressedTrace(memory_trace_file, uncompressed_trace, 302 memory_trace_size); 303 m_mem_vec->populatePages(uncompressed_trace); 304 305 delete [] uncompressed_trace; 306 uncompressed_trace = NULL; 307 } 308 309 string cache_trace_file; 310 uint64 cache_trace_size = 0; 311 312 UNSERIALIZE_SCALAR(cache_trace_file); 313 UNSERIALIZE_SCALAR(cache_trace_size); 314 cache_trace_file = cp->cptDir + "/" + cache_trace_file; 315 316 readCompressedTrace(cache_trace_file, uncompressed_trace, 317 cache_trace_size); 318 m_warmup_enabled = true; 319 320 vector<Sequencer*> sequencer_map; 321 Sequencer* t = NULL; 322 for (int cntrl = 0; cntrl < m_abs_cntrl_vec.size(); cntrl++) { 323 sequencer_map.push_back(m_abs_cntrl_vec[cntrl]->getSequencer()); 324 if (t == NULL) t = sequencer_map[cntrl]; 325 } 326 327 assert(t != NULL); 328 329 for (int cntrl = 0; cntrl < m_abs_cntrl_vec.size(); cntrl++) { 330 if (sequencer_map[cntrl] == NULL) { 331 sequencer_map[cntrl] = t; 332 } 333 } 334 335 m_cache_recorder = new CacheRecorder(uncompressed_trace, cache_trace_size, 336 sequencer_map, block_size_bytes); 337} 338 339void 340RubySystem::startup() 341{ 342 343 // Ruby restores state from a checkpoint by resetting the clock to 0 and 344 // playing the requests that can possibly re-generate the cache state. 345 // The clock value is set to the actual checkpointed value once all the 346 // requests have been executed. 347 // 348 // This way of restoring state is pretty finicky. For example, if a 349 // Ruby component reads time before the state has been restored, it would 350 // cache this value and hence its clock would not be reset to 0, when 351 // Ruby resets the global clock. This can potentially result in a 352 // deadlock. 353 // 354 // The solution is that no Ruby component should read time before the 355 // simulation starts. And then one also needs to hope that the time 356 // Ruby finishes restoring the state is less than the time when the 357 // state was checkpointed. 358 359 if (m_warmup_enabled) { 360 // save the current tick value 361 Tick curtick_original = curTick(); 362 // save the event queue head 363 Event* eventq_head = eventq->replaceHead(NULL); 364 // set curTick to 0 and reset Ruby System's clock 365 setCurTick(0); 366 resetClock(); 367 368 // Schedule an event to start cache warmup 369 enqueueRubyEvent(curTick()); 370 simulate(); 371 372 delete m_cache_recorder; 373 m_cache_recorder = NULL; 374 m_warmup_enabled = false; 375 376 // reset DRAM so that it's not waiting for events on the old event 377 // queue 378 for (int i = 0; i < m_memory_controller_vec.size(); ++i) { 379 m_memory_controller_vec[i]->reset(); 380 } 381 382 // Restore eventq head 383 eventq_head = eventq->replaceHead(eventq_head); 384 // Restore curTick and Ruby System's clock 385 setCurTick(curtick_original); 386 resetClock(); 387 } 388 389 resetStats(); 390} 391 392void 393RubySystem::RubyEvent::process() 394{ 395 if (ruby_system->m_warmup_enabled) { 396 ruby_system->m_cache_recorder->enqueueNextFetchRequest(); 397 } else if (ruby_system->m_cooldown_enabled) { 398 ruby_system->m_cache_recorder->enqueueNextFlushRequest(); 399 } 400} 401 402void 403RubySystem::resetStats() 404{ 405 g_ruby_start = curCycle(); 406} 407 408bool 409RubySystem::functionalRead(PacketPtr pkt) 410{ 411 Address address(pkt->getAddr()); 412 Address line_address(address); 413 line_address.makeLineAddress(); 414 415 AccessPermission access_perm = AccessPermission_NotPresent; 416 int num_controllers = m_abs_cntrl_vec.size(); 417 418 DPRINTF(RubySystem, "Functional Read request for %s\n",address); 419 420 unsigned int num_ro = 0; 421 unsigned int num_rw = 0; 422 unsigned int num_busy = 0; 423 unsigned int num_backing_store = 0; 424 unsigned int num_invalid = 0; 425 426 // In this loop we count the number of controllers that have the given 427 // address in read only, read write and busy states. 428 for (unsigned int i = 0; i < num_controllers; ++i) { 429 access_perm = m_abs_cntrl_vec[i]-> getAccessPermission(line_address); 430 if (access_perm == AccessPermission_Read_Only) 431 num_ro++; 432 else if (access_perm == AccessPermission_Read_Write) 433 num_rw++; 434 else if (access_perm == AccessPermission_Busy) 435 num_busy++; 436 else if (access_perm == AccessPermission_Backing_Store) 437 // See RubySlicc_Exports.sm for details, but Backing_Store is meant 438 // to represent blocks in memory *for Broadcast/Snooping protocols*, 439 // where memory has no idea whether it has an exclusive copy of data 440 // or not. 441 num_backing_store++; 442 else if (access_perm == AccessPermission_Invalid || 443 access_perm == AccessPermission_NotPresent) 444 num_invalid++; 445 } 446 assert(num_rw <= 1); 447 448 uint8_t *data = pkt->getPtr<uint8_t>(true); 449 unsigned int size_in_bytes = pkt->getSize(); 450 unsigned startByte = address.getAddress() - line_address.getAddress(); 451 452 // This if case is meant to capture what happens in a Broadcast/Snoop 453 // protocol where the block does not exist in the cache hierarchy. You 454 // only want to read from the Backing_Store memory if there is no copy in 455 // the cache hierarchy, otherwise you want to try to read the RO or RW 456 // copies existing in the cache hierarchy (covered by the else statement). 457 // The reason is because the Backing_Store memory could easily be stale, if 458 // there are copies floating around the cache hierarchy, so you want to read 459 // it only if it's not in the cache hierarchy at all. 460 if (num_invalid == (num_controllers - 1) && 461 num_backing_store == 1) { 462 DPRINTF(RubySystem, "only copy in Backing_Store memory, read from it\n"); 463 for (unsigned int i = 0; i < num_controllers; ++i) { 464 access_perm = m_abs_cntrl_vec[i]->getAccessPermission(line_address); 465 if (access_perm == AccessPermission_Backing_Store) { 466 DataBlock& block = m_abs_cntrl_vec[i]-> 467 getDataBlock(line_address); 468 469 DPRINTF(RubySystem, "reading from %s block %s\n", 470 m_abs_cntrl_vec[i]->name(), block); 471 for (unsigned j = 0; j < size_in_bytes; ++j) { 472 data[j] = block.getByte(j + startByte); 473 } 474 return true; 475 } 476 } 477 } else if (num_ro > 0 || num_rw == 1) { 478 // In Broadcast/Snoop protocols, this covers if you know the block 479 // exists somewhere in the caching hierarchy, then you want to read any 480 // valid RO or RW block. In directory protocols, same thing, you want 481 // to read any valid readable copy of the block. 482 DPRINTF(RubySystem, "num_busy = %d, num_ro = %d, num_rw = %d\n", 483 num_busy, num_ro, num_rw); 484 // In this loop, we try to figure which controller has a read only or 485 // a read write copy of the given address. Any valid copy would suffice 486 // for a functional read. 487 for (unsigned int i = 0;i < num_controllers;++i) { 488 access_perm = m_abs_cntrl_vec[i]->getAccessPermission(line_address); 489 if (access_perm == AccessPermission_Read_Only || 490 access_perm == AccessPermission_Read_Write) { 491 DataBlock& block = m_abs_cntrl_vec[i]-> 492 getDataBlock(line_address); 493 494 DPRINTF(RubySystem, "reading from %s block %s\n", 495 m_abs_cntrl_vec[i]->name(), block); 496 for (unsigned j = 0; j < size_in_bytes; ++j) { 497 data[j] = block.getByte(j + startByte); 498 } 499 return true; 500 } 501 } 502 } 503 504 return false; 505} 506 507// The function searches through all the buffers that exist in different 508// cache, directory and memory controllers, and in the network components 509// and writes the data portion of those that hold the address specified 510// in the packet. 511bool 512RubySystem::functionalWrite(PacketPtr pkt) 513{ 514 Address addr(pkt->getAddr()); 515 Address line_addr = line_address(addr); 516 AccessPermission access_perm = AccessPermission_NotPresent; 517 int num_controllers = m_abs_cntrl_vec.size(); 518 519 DPRINTF(RubySystem, "Functional Write request for %s\n",addr); 520 521 uint8_t *data = pkt->getPtr<uint8_t>(true); 522 unsigned int size_in_bytes = pkt->getSize(); 523 unsigned startByte = addr.getAddress() - line_addr.getAddress(); 524 525 uint32_t M5_VAR_USED num_functional_writes = 0; 526 527 for (unsigned int i = 0; i < num_controllers;++i) { 528 num_functional_writes += 529 m_abs_cntrl_vec[i]->functionalWriteBuffers(pkt); 530 531 access_perm = m_abs_cntrl_vec[i]->getAccessPermission(line_addr); 532 if (access_perm != AccessPermission_Invalid && 533 access_perm != AccessPermission_NotPresent) { 534 535 num_functional_writes++; 536 537 DataBlock& block = m_abs_cntrl_vec[i]->getDataBlock(line_addr); 538 DPRINTF(RubySystem, "%s\n",block); 539 for (unsigned j = 0; j < size_in_bytes; ++j) { 540 block.setByte(j + startByte, data[j]); 541 } 542 DPRINTF(RubySystem, "%s\n",block); 543 } 544 } 545 546 for (unsigned int i = 0; i < m_memory_controller_vec.size() ;++i) { 547 num_functional_writes += 548 m_memory_controller_vec[i]->functionalWriteBuffers(pkt); 549 } 550 551 num_functional_writes += m_network->functionalWrite(pkt); 552 DPRINTF(RubySystem, "Messages written = %u\n", num_functional_writes); 553 554 return true; 555} 556 557#ifdef CHECK_COHERENCE 558// This code will check for cases if the given cache block is exclusive in 559// one node and shared in another-- a coherence violation 560// 561// To use, the SLICC specification must call sequencer.checkCoherence(address) 562// when the controller changes to a state with new permissions. Do this 563// in setState. The SLICC spec must also define methods "isBlockShared" 564// and "isBlockExclusive" that are specific to that protocol 565// 566void 567RubySystem::checkGlobalCoherenceInvariant(const Address& addr) 568{ 569#if 0 570 NodeID exclusive = -1; 571 bool sharedDetected = false; 572 NodeID lastShared = -1; 573 574 for (int i = 0; i < m_chip_vector.size(); i++) { 575 if (m_chip_vector[i]->isBlockExclusive(addr)) { 576 if (exclusive != -1) { 577 // coherence violation 578 WARN_EXPR(exclusive); 579 WARN_EXPR(m_chip_vector[i]->getID()); 580 WARN_EXPR(addr); 581 WARN_EXPR(getTime()); 582 ERROR_MSG("Coherence Violation Detected -- 2 exclusive chips"); 583 } else if (sharedDetected) { 584 WARN_EXPR(lastShared); 585 WARN_EXPR(m_chip_vector[i]->getID()); 586 WARN_EXPR(addr); 587 WARN_EXPR(getTime()); 588 ERROR_MSG("Coherence Violation Detected -- exclusive chip with >=1 shared"); 589 } else { 590 exclusive = m_chip_vector[i]->getID(); 591 } 592 } else if (m_chip_vector[i]->isBlockShared(addr)) { 593 sharedDetected = true; 594 lastShared = m_chip_vector[i]->getID(); 595 596 if (exclusive != -1) { 597 WARN_EXPR(lastShared); 598 WARN_EXPR(exclusive); 599 WARN_EXPR(addr); 600 WARN_EXPR(getTime()); 601 ERROR_MSG("Coherence Violation Detected -- exclusive chip with >=1 shared"); 602 } 603 } 604 } 605#endif 606} 607#endif 608 609RubySystem * 610RubySystemParams::create() 611{ 612 return new RubySystem(this); 613} 614