2 * Copyright (c) 1999-2011 Mark D. Hill and David A. Wood
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.
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.
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"
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
;
54 Network
* RubySystem::m_network_ptr
;
55 Profiler
* RubySystem::m_profiler_ptr
;
56 MemoryVector
* RubySystem::m_mem_vec_ptr
;
58 RubySystem::RubySystem(const Params
*p
)
61 if (g_system_ptr
!= NULL
)
62 fatal("Only one RubySystem object currently allowed.\n");
64 m_random_seed
= p
->random_seed
;
65 srandom(m_random_seed
);
66 m_randomization
= p
->randomization
;
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
);
73 m_memory_size_bytes
= p
->mem_size
;
74 if (m_memory_size_bytes
== 0) {
75 m_memory_size_bits
= 0;
77 m_memory_size_bits
= floorLog2(m_memory_size_bytes
);
80 g_eventQueue_ptr
= new RubyEventQueue(p
->eventq
, m_clock
);
85 m_mem_vec_ptr
= new MemoryVector
;
86 m_mem_vec_ptr
->resize(m_memory_size_bytes
);
90 // Print ruby configuration and stats at exit
92 RubyExitCallback
* rubyExitCB
= new RubyExitCallback(p
->stats_filename
);
93 registerExitCallback(rubyExitCB
);
94 m_warmup_enabled
= false;
95 m_cooldown_enabled
= false;
101 m_profiler_ptr
->clearStats();
105 RubySystem::registerNetwork(Network
* network_ptr
)
107 m_network_ptr
= network_ptr
;
111 RubySystem::registerProfiler(Profiler
* profiler_ptr
)
113 m_profiler_ptr
= profiler_ptr
;
117 RubySystem::registerAbstractController(AbstractController
* cntrl
)
119 m_abs_cntrl_vec
.push_back(cntrl
);
123 RubySystem::registerSparseMemory(SparseMemory
* s
)
125 m_sparse_memory_vector
.push_back(s
);
128 RubySystem::~RubySystem()
130 delete m_network_ptr
;
131 delete m_profiler_ptr
;
133 delete m_mem_vec_ptr
;
137 RubySystem::printSystemConfig(ostream
& out
)
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
;
150 RubySystem::printConfig(ostream
& out
)
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";
160 RubySystem::printStats(ostream
& out
)
162 const time_t T
= time(NULL
);
163 tm
*localTime
= localtime(&T
);
165 strftime(buf
, 100, "%b/%d/%Y %H:%M:%S", localTime
);
167 out
<< "Real time: " << buf
<< endl
;
169 m_profiler_ptr
->printStats(out
);
170 m_network_ptr
->printStats(out
);
174 RubySystem::writeCompressedTrace(uint8
* raw_data
, string filename
,
175 uint64 uncompressed_trace_size
)
177 // Create the checkpoint file for the memory
178 string thefile
= Checkpoint::dir() + "/" + filename
.c_str();
180 int fd
= creat(thefile
.c_str(), 0664);
183 fatal("Can't open memory trace file '%s'\n", filename
);
186 gzFile compressedMemory
= gzdopen(fd
, "wb");
187 if (compressedMemory
== NULL
)
188 fatal("Insufficient memory to allocate compression state for %s\n",
191 if (gzwrite(compressedMemory
, raw_data
, uncompressed_trace_size
) !=
192 uncompressed_trace_size
) {
193 fatal("Write failed on memory trace file '%s'\n", filename
);
196 if (gzclose(compressedMemory
)) {
197 fatal("Close failed on memory trace file '%s'\n", filename
);
203 RubySystem::serialize(std::ostream
&os
)
205 m_cooldown_enabled
= true;
207 vector
<Sequencer
*> sequencer_map
;
208 Sequencer
* sequencer_ptr
= NULL
;
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
];
220 assert(sequencer_ptr
!= NULL
);
222 for (int cntrl
= 0; cntrl
< m_abs_cntrl_vec
.size(); cntrl
++) {
223 if (sequencer_map
[cntrl
] == NULL
) {
224 sequencer_map
[cntrl
] = sequencer_ptr
;
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
);
232 for (int cntrl
= 0; cntrl
< m_abs_cntrl_vec
.size(); cntrl
++) {
233 m_abs_cntrl_vec
[cntrl
]->recordCacheTrace(cntrl
, m_cache_recorder
);
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",
244 // Schedule an event to start cache cooldown
245 DPRINTF(RubyCacheTrace
, "Starting cache flush\n");
246 enqueueRubyEvent(curTick());
248 DPRINTF(RubyCacheTrace
, "Cache flush complete\n");
250 // Restore eventq head
251 eventq_head
= eventq
->replaceHead(eventq_head
);
253 curTick(curtick_original
);
255 uint8
* raw_data
= NULL
;
257 if (m_mem_vec_ptr
!= NULL
) {
258 uint64 memory_trace_size
= m_mem_vec_ptr
->collatePages(raw_data
);
260 string memory_trace_file
= name() + ".memory.gz";
261 writeCompressedTrace(raw_data
, memory_trace_file
,
264 SERIALIZE_SCALAR(memory_trace_file
);
265 SERIALIZE_SCALAR(memory_trace_size
);
268 for (int i
= 0; i
< m_sparse_memory_vector
.size(); ++i
) {
269 m_sparse_memory_vector
[i
]->recordBlocks(cntrl_id
,
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
,
278 string cache_trace_file
= name() + ".cache.gz";
279 writeCompressedTrace(raw_data
, cache_trace_file
, cache_trace_size
);
281 SERIALIZE_SCALAR(cache_trace_file
);
282 SERIALIZE_SCALAR(cache_trace_size
);
284 m_cooldown_enabled
= false;
288 RubySystem::readCompressedTrace(string filename
, uint8
*& raw_data
,
289 uint64
& uncompressed_trace_size
)
291 // Read the trace file
292 gzFile compressedTrace
;
295 int fd
= open(filename
.c_str(), O_RDONLY
);
298 fatal("Unable to open trace file %s", filename
);
301 compressedTrace
= gzdopen(fd
, "rb");
302 if (compressedTrace
== NULL
) {
303 fatal("Insufficient memory to allocate compression state for %s\n",
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
);
313 if (gzclose(compressedTrace
)) {
314 fatal("Failed to close cache trace file '%s'\n", filename
);
319 RubySystem::unserialize(Checkpoint
*cp
, const string
§ion
)
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()
327 uint8
* uncompressed_trace
= NULL
;
329 if (m_mem_vec_ptr
!= NULL
) {
330 string memory_trace_file
;
331 uint64 memory_trace_size
= 0;
333 UNSERIALIZE_SCALAR(memory_trace_file
);
334 UNSERIALIZE_SCALAR(memory_trace_size
);
335 memory_trace_file
= cp
->cptDir
+ "/" + memory_trace_file
;
337 readCompressedTrace(memory_trace_file
, uncompressed_trace
,
339 m_mem_vec_ptr
->populatePages(uncompressed_trace
);
341 delete uncompressed_trace
;
342 uncompressed_trace
= NULL
;
345 string cache_trace_file
;
346 uint64 cache_trace_size
= 0;
348 UNSERIALIZE_SCALAR(cache_trace_file
);
349 UNSERIALIZE_SCALAR(cache_trace_size
);
350 cache_trace_file
= cp
->cptDir
+ "/" + cache_trace_file
;
352 readCompressedTrace(cache_trace_file
, uncompressed_trace
,
354 m_warmup_enabled
= true;
356 vector
<Sequencer
*> sequencer_map
;
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
];
365 for (int cntrl
= 0; cntrl
< m_abs_cntrl_vec
.size(); cntrl
++) {
366 if (sequencer_map
[cntrl
] == NULL
) {
367 sequencer_map
[cntrl
] = t
;
371 m_cache_recorder
= new CacheRecorder(uncompressed_trace
, cache_trace_size
,
376 RubySystem::startup()
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
);
386 // Schedule an event to start cache warmup
387 enqueueRubyEvent(curTick());
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
);
396 curTick(curtick_original
);
401 RubySystem::RubyEvent::process()
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();
411 RubySystem::clearStats() const
413 m_profiler_ptr
->clearStats();
414 m_network_ptr
->clearStats();
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
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
427 RubySystem::checkGlobalCoherenceInvariant(const Address
& addr
)
430 NodeID exclusive
= -1;
431 bool sharedDetected
= false;
432 NodeID lastShared
= -1;
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());
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());
447 WARN_EXPR(g_eventQueue_ptr
->getTime());
448 ERROR_MSG("Coherence Violation Detected -- exclusive chip with >=1 shared");
450 exclusive
= m_chip_vector
[i
]->getID();
452 } else if (m_chip_vector
[i
]->isBlockShared(addr
)) {
453 sharedDetected
= true;
454 lastShared
= m_chip_vector
[i
]->getID();
456 if (exclusive
!= -1) {
457 WARN_EXPR(lastShared
);
458 WARN_EXPR(exclusive
);
460 WARN_EXPR(g_eventQueue_ptr
->getTime());
461 ERROR_MSG("Coherence Violation Detected -- exclusive chip with >=1 shared");
470 RubySystemParams::create()
472 return new RubySystem(this);
476 * virtual process function that is invoked when the callback
480 RubyExitCallback::process()
482 std::ostream
*os
= simout
.create(stats_filename
);
483 RubySystem::printConfig(*os
);
485 RubySystem::printStats(*os
);