#include <zlib.h>
#include <cstdio>
+#include <list>
#include "base/intmath.hh"
#include "base/statistics.hh"
#include "mem/ruby/common/Address.hh"
#include "mem/ruby/network/Network.hh"
#include "mem/ruby/system/System.hh"
+#include "mem/simple_mem.hh"
#include "sim/eventq.hh"
#include "sim/simulate.hh"
uint32_t RubySystem::m_block_size_bytes;
uint32_t RubySystem::m_block_size_bits;
uint32_t RubySystem::m_memory_size_bits;
+bool RubySystem::m_warmup_enabled = false;
+// To look forward to allowing multiple RubySystem instances, track the number
+// of RubySystems that need to be warmed up on checkpoint restore.
+unsigned RubySystem::m_systems_to_warmup = 0;
+bool RubySystem::m_cooldown_enabled = false;
RubySystem::RubySystem(const Params *p)
- : ClockedObject(p)
+ : ClockedObject(p), m_access_backing_store(p->access_backing_store),
+ m_cache_recorder(NULL)
{
- if (g_system_ptr != NULL)
- fatal("Only one RubySystem object currently allowed.\n");
-
m_random_seed = p->random_seed;
srandom(m_random_seed);
m_randomization = p->randomization;
m_block_size_bits = floorLog2(m_block_size_bytes);
m_memory_size_bits = p->memory_size_bits;
- m_warmup_enabled = false;
- m_cooldown_enabled = false;
-
- // Setup the global variables used in Ruby
- g_system_ptr = this;
-
// Resize to the size of different machine types
- g_abs_controls.resize(MachineType_NUM);
+ m_abstract_controls.resize(MachineType_NUM);
// Collate the statistics before they are printed.
Stats::registerDumpCallback(new RubyStatsCallback(this));
// Create the profiler
- m_profiler = new Profiler(p);
+ m_profiler = new Profiler(p, this);
m_phys_mem = p->phys_mem;
}
void
RubySystem::registerNetwork(Network* network_ptr)
{
- m_network = network_ptr;
+ m_network = network_ptr;
}
void
RubySystem::registerAbstractController(AbstractController* cntrl)
{
- m_abs_cntrl_vec.push_back(cntrl);
+ m_abs_cntrl_vec.push_back(cntrl);
- MachineID id = cntrl->getMachineID();
- g_abs_controls[id.getType()][id.getNum()] = cntrl;
+ MachineID id = cntrl->getMachineID();
+ m_abstract_controls[id.getType()][id.getNum()] = cntrl;
}
RubySystem::~RubySystem()
}
void
-RubySystem::writeCompressedTrace(uint8_t *raw_data, string filename,
- uint64 uncompressed_trace_size)
+RubySystem::makeCacheRecorder(uint8_t *uncompressed_trace,
+ uint64 cache_trace_size,
+ uint64 block_size_bytes)
{
- // Create the checkpoint file for the memory
- string thefile = Checkpoint::dir() + "/" + filename.c_str();
-
- int fd = creat(thefile.c_str(), 0664);
- if (fd < 0) {
- perror("creat");
- fatal("Can't open memory trace file '%s'\n", filename);
- }
-
- gzFile compressedMemory = gzdopen(fd, "wb");
- if (compressedMemory == NULL)
- fatal("Insufficient memory to allocate compression state for %s\n",
- filename);
-
- if (gzwrite(compressedMemory, raw_data, uncompressed_trace_size) !=
- uncompressed_trace_size) {
- fatal("Write failed on memory trace file '%s'\n", filename);
- }
-
- if (gzclose(compressedMemory)) {
- fatal("Close failed on memory trace file '%s'\n", filename);
- }
- delete[] raw_data;
-}
-
-void
-RubySystem::serialize(std::ostream &os)
-{
- m_cooldown_enabled = true;
vector<Sequencer*> sequencer_map;
Sequencer* sequencer_ptr = NULL;
}
}
- // Store the cache-block size, so we are able to restore on systems with a
- // different cache-block size. CacheRecorder depends on the correct
- // cache-block size upon unserializing.
- uint64 block_size_bytes = getBlockSizeBytes();
- SERIALIZE_SCALAR(block_size_bytes);
+ // Remove the old CacheRecorder if it's still hanging about.
+ if (m_cache_recorder != NULL) {
+ delete m_cache_recorder;
+ }
- DPRINTF(RubyCacheTrace, "Recording Cache Trace\n");
// Create the CacheRecorder and record the cache trace
- m_cache_recorder = new CacheRecorder(NULL, 0, sequencer_map,
- block_size_bytes);
+ m_cache_recorder = new CacheRecorder(uncompressed_trace, cache_trace_size,
+ sequencer_map, block_size_bytes);
+}
+void
+RubySystem::memWriteback()
+{
+ m_cooldown_enabled = true;
+
+ // Make the trace so we know what to write back.
+ DPRINTF(RubyCacheTrace, "Recording Cache Trace\n");
+ makeCacheRecorder(NULL, 0, getBlockSizeBytes());
for (int cntrl = 0; cntrl < m_abs_cntrl_vec.size(); cntrl++) {
m_abs_cntrl_vec[cntrl]->recordCacheTrace(cntrl, m_cache_recorder);
}
-
DPRINTF(RubyCacheTrace, "Cache Trace Complete\n");
+
// save the current tick value
Tick curtick_original = curTick();
- // save the event queue head
- Event* eventq_head = eventq->replaceHead(NULL);
- DPRINTF(RubyCacheTrace, "Recording current tick %ld and event queue\n",
- curtick_original);
+ DPRINTF(RubyCacheTrace, "Recording current tick %ld\n", curtick_original);
+
+ // Deschedule all prior events on the event queue, but record the tick they
+ // were scheduled at so they can be restored correctly later.
+ list<pair<Event*, Tick> > original_events;
+ while (!eventq->empty()) {
+ Event *curr_head = eventq->getHead();
+ if (curr_head->isAutoDelete()) {
+ DPRINTF(RubyCacheTrace, "Event %s auto-deletes when descheduled,"
+ " not recording\n", curr_head->name());
+ } else {
+ original_events.push_back(make_pair(curr_head, curr_head->when()));
+ }
+ eventq->deschedule(curr_head);
+ }
// Schedule an event to start cache cooldown
DPRINTF(RubyCacheTrace, "Starting cache flush\n");
simulate();
DPRINTF(RubyCacheTrace, "Cache flush complete\n");
- // Restore eventq head
- eventq_head = eventq->replaceHead(eventq_head);
+ // Deschedule any events left on the event queue.
+ while (!eventq->empty()) {
+ eventq->deschedule(eventq->getHead());
+ }
+
// Restore curTick
setCurTick(curtick_original);
- // Aggergate the trace entries together into a single array
+ // Restore all events that were originally on the event queue. This is
+ // done after setting curTick back to its original value so that events do
+ // not seem to be scheduled in the past.
+ while (!original_events.empty()) {
+ pair<Event*, Tick> event = original_events.back();
+ eventq->schedule(event.first, event.second);
+ original_events.pop_back();
+ }
+
+ // No longer flushing back to memory.
+ m_cooldown_enabled = false;
+
+ // There are several issues with continuing simulation after calling
+ // memWriteback() at the moment, that stem from taking events off the
+ // queue, simulating again, and then putting them back on, whilst
+ // pretending that no time has passed. One is that some events will have
+ // been deleted, so can't be put back. Another is that any object
+ // recording the tick something happens may end up storing a tick in the
+ // future. A simple warning here alerts the user that things may not work
+ // as expected.
+ warn_once("Ruby memory writeback is experimental. Continuing simulation "
+ "afterwards may not always work as intended.");
+
+ // Keep the cache recorder around so that we can dump the trace if a
+ // checkpoint is immediately taken.
+}
+
+void
+RubySystem::writeCompressedTrace(uint8_t *raw_data, string filename,
+ uint64 uncompressed_trace_size)
+{
+ // Create the checkpoint file for the memory
+ string thefile = CheckpointIn::dir() + "/" + filename.c_str();
+
+ int fd = creat(thefile.c_str(), 0664);
+ if (fd < 0) {
+ perror("creat");
+ fatal("Can't open memory trace file '%s'\n", filename);
+ }
+
+ gzFile compressedMemory = gzdopen(fd, "wb");
+ if (compressedMemory == NULL)
+ fatal("Insufficient memory to allocate compression state for %s\n",
+ filename);
+
+ if (gzwrite(compressedMemory, raw_data, uncompressed_trace_size) !=
+ uncompressed_trace_size) {
+ fatal("Write failed on memory trace file '%s'\n", filename);
+ }
+
+ if (gzclose(compressedMemory)) {
+ fatal("Close failed on memory trace file '%s'\n", filename);
+ }
+ delete[] raw_data;
+}
+
+void
+RubySystem::serializeOld(CheckpointOut &cp)
+{
+ // Store the cache-block size, so we are able to restore on systems with a
+ // different cache-block size. CacheRecorder depends on the correct
+ // cache-block size upon unserializing.
+ uint64 block_size_bytes = getBlockSizeBytes();
+ SERIALIZE_SCALAR(block_size_bytes);
+
+ // Check that there's a valid trace to use. If not, then memory won't be
+ // up-to-date and the simulation will probably fail when restoring from the
+ // checkpoint.
+ if (m_cache_recorder == NULL) {
+ fatal("Call memWriteback() before serialize() to create ruby trace");
+ }
+
+ // Aggregate the trace entries together into a single array
uint8_t *raw_data = new uint8_t[4096];
uint64 cache_trace_size = m_cache_recorder->aggregateRecords(&raw_data,
4096);
SERIALIZE_SCALAR(cache_trace_file);
SERIALIZE_SCALAR(cache_trace_size);
- m_cooldown_enabled = false;
+ // Now finished with the cache recorder.
+ delete m_cache_recorder;
+ m_cache_recorder = NULL;
}
void
}
void
-RubySystem::unserialize(Checkpoint *cp, const string §ion)
+RubySystem::unserialize(CheckpointIn &cp)
{
uint8_t *uncompressed_trace = NULL;
UNSERIALIZE_SCALAR(cache_trace_file);
UNSERIALIZE_SCALAR(cache_trace_size);
- cache_trace_file = cp->cptDir + "/" + cache_trace_file;
+ cache_trace_file = cp.cptDir + "/" + cache_trace_file;
readCompressedTrace(cache_trace_file, uncompressed_trace,
cache_trace_size);
m_warmup_enabled = true;
+ m_systems_to_warmup++;
- vector<Sequencer*> sequencer_map;
- Sequencer* t = NULL;
- for (int cntrl = 0; cntrl < m_abs_cntrl_vec.size(); cntrl++) {
- sequencer_map.push_back(m_abs_cntrl_vec[cntrl]->getSequencer());
- if (t == NULL) t = sequencer_map[cntrl];
- }
-
- assert(t != NULL);
-
- for (int cntrl = 0; cntrl < m_abs_cntrl_vec.size(); cntrl++) {
- if (sequencer_map[cntrl] == NULL) {
- sequencer_map[cntrl] = t;
- }
- }
-
- m_cache_recorder = new CacheRecorder(uncompressed_trace, cache_trace_size,
- sequencer_map, block_size_bytes);
+ // Create the cache recorder that will hang around until startup.
+ makeCacheRecorder(uncompressed_trace, cache_trace_size, block_size_bytes);
}
void
// state was checkpointed.
if (m_warmup_enabled) {
+ DPRINTF(RubyCacheTrace, "Starting ruby cache warmup\n");
// save the current tick value
Tick curtick_original = curTick();
// save the event queue head
delete m_cache_recorder;
m_cache_recorder = NULL;
- m_warmup_enabled = false;
+ m_systems_to_warmup--;
+ if (m_systems_to_warmup == 0) {
+ m_warmup_enabled = false;
+ }
// Restore eventq head
eventq_head = eventq->replaceHead(eventq_head);
void
RubySystem::RubyEvent::process()
{
- if (ruby_system->m_warmup_enabled) {
- ruby_system->m_cache_recorder->enqueueNextFetchRequest();
- } else if (ruby_system->m_cooldown_enabled) {
- ruby_system->m_cache_recorder->enqueueNextFlushRequest();
+ if (RubySystem::getWarmupEnabled()) {
+ m_ruby_system->m_cache_recorder->enqueueNextFetchRequest();
+ } else if (RubySystem::getCooldownEnabled()) {
+ m_ruby_system->m_cache_recorder->enqueueNextFlushRequest();
}
}
void
RubySystem::resetStats()
{
- g_ruby_start = curCycle();
+ m_start_cycle = curCycle();
}
bool