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 &section)
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