gem5 v24.1.0.1
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
RubySystem.cc
Go to the documentation of this file.
1/*
2 * Copyright (c) 2019,2021 ARM Limited
3 * All rights reserved.
4 *
5 * The license below extends only to copyright in the software and shall
6 * not be construed as granting a license to any other intellectual
7 * property including but not limited to intellectual property relating
8 * to a hardware implementation of the functionality of the software
9 * licensed hereunder. You may use the software subject to the license
10 * terms below provided that you ensure that this notice is replicated
11 * unmodified and in its entirety in all distributions of the software,
12 * modified or unmodified, in source code or in binary form.
13 *
14 * Copyright (c) 1999-2011 Mark D. Hill and David A. Wood
15 * All rights reserved.
16 *
17 * Redistribution and use in source and binary forms, with or without
18 * modification, are permitted provided that the following conditions are
19 * met: redistributions of source code must retain the above copyright
20 * notice, this list of conditions and the following disclaimer;
21 * redistributions in binary form must reproduce the above copyright
22 * notice, this list of conditions and the following disclaimer in the
23 * documentation and/or other materials provided with the distribution;
24 * neither the name of the copyright holders nor the names of its
25 * contributors may be used to endorse or promote products derived from
26 * this software without specific prior written permission.
27 *
28 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
31 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
32 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
33 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
34 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
35 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
36 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
38 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 */
40
42
43#include <fcntl.h>
44#include <zlib.h>
45
46#include <cstdio>
47#include <list>
48
49#include "base/compiler.hh"
50#include "base/intmath.hh"
51#include "base/statistics.hh"
52#include "debug/RubyCacheTrace.hh"
53#include "debug/RubySystem.hh"
58#include "mem/simple_mem.hh"
59#include "sim/eventq.hh"
60#include "sim/simulate.hh"
61#include "sim/system.hh"
62
63namespace gem5
64{
65
66namespace ruby
67{
68
69// To look forward to allowing multiple RubySystem instances, track the number
70// of RubySystems that need to be warmed up on checkpoint restore.
71
73 : ClockedObject(p), m_access_backing_store(p.access_backing_store),
74 m_cache_recorder(NULL)
75{
76 m_randomization = p.randomization;
77
78 m_block_size_bytes = p.block_size_bytes;
81 m_memory_size_bits = p.memory_size_bits;
82
83 // Resize to the size of different machine types
84 m_abstract_controls.resize(MachineType_NUM);
85
86 // Collate the statistics before they are printed.
88 // Create the profiler
89 m_profiler = new Profiler(p, this);
90 m_phys_mem = p.phys_mem;
91}
92
93void
95{
96 m_networks.emplace_back(network_ptr);
97}
98
99void
101 AbstractController* cntrl, std::unique_ptr<ProtocolInfo> cntl_protocol)
102{
103 m_abs_cntrl_vec.push_back(cntrl);
104
105 MachineID id = cntrl->getMachineID();
106 m_abstract_controls[id.getType()][id.getNum()] = cntrl;
107
108 if (!protocolInfo) {
109 protocolInfo = std::move(cntl_protocol);
110 } else {
111 fatal_if(
112 protocolInfo->getName() != cntl_protocol->getName(),
113 "All controllers in a system must use the same protocol. %s != %s",
114 protocolInfo->getName().c_str(), cntl_protocol->getName().c_str()
115 );
116 }
117}
118
119void
121{
122 int network_id = -1;
123 for (int idx = 0; idx < m_networks.size(); ++idx) {
124 if (m_networks[idx].get() == network) {
125 network_id = idx;
126 }
127 }
128
129 fatal_if(network_id < 0, "Could not add MachineID %s. Network not found",
130 MachineIDToString(mach_id).c_str());
131
132 machineToNetwork.insert(std::make_pair(mach_id, network_id));
133}
134
135// This registers all requestor IDs in the system for functional reads. This
136// should be called in init() since requestor IDs are obtained in a SimObject's
137// constructor and there are functional reads/writes between init() and
138// startup().
139void
141{
142 // Create the map for RequestorID to network node. This is done in init()
143 // because all RequestorIDs must be obtained in the constructor and
144 // AbstractControllers are registered in their constructor. This is done
145 // in two steps: (1) Add all of the AbstractControllers. Since we don't
146 // have a mapping of RequestorID to MachineID this is the easiest way to
147 // filter out AbstractControllers from non-Ruby requestors. (2) Go through
148 // the system's list of RequestorIDs and add missing RequestorIDs to
149 // network 0 (the default).
150 for (auto& cntrl : m_abs_cntrl_vec) {
151 RequestorID id = cntrl->getRequestorId();
152 MachineID mach_id = cntrl->getMachineID();
153
154 // These are setup in Network constructor and should exist
155 fatal_if(!machineToNetwork.count(mach_id),
156 "No machineID %s. Does not belong to a Ruby network?",
157 MachineIDToString(mach_id).c_str());
158
159 auto network_id = machineToNetwork[mach_id];
160 requestorToNetwork.insert(std::make_pair(id, network_id));
161
162 // Create helper vectors for each network to iterate over.
163 netCntrls[network_id].push_back(cntrl);
164 }
165
166 // Default all other requestor IDs to network 0
167 for (auto id = 0; id < params().system->maxRequestors(); ++id) {
168 if (!requestorToNetwork.count(id)) {
169 requestorToNetwork.insert(std::make_pair(id, 0));
170 }
171 }
172}
173
175{
176 delete m_profiler;
177}
178
179void
180RubySystem::makeCacheRecorder(uint8_t *uncompressed_trace,
181 uint64_t cache_trace_size,
182 uint64_t block_size_bytes)
183{
184 std::vector<RubyPort*> ruby_port_map;
185 RubyPort* ruby_port_ptr = NULL;
186
187 for (int cntrl = 0; cntrl < m_abs_cntrl_vec.size(); cntrl++) {
188 if (m_abs_cntrl_vec[cntrl]->getGPUCoalescer() != NULL) {
189 ruby_port_map.push_back(
190 (RubyPort*)m_abs_cntrl_vec[cntrl]->getGPUCoalescer());
191 } else {
192 ruby_port_map.push_back(
193 (RubyPort*)m_abs_cntrl_vec[cntrl]->getCPUSequencer());
194 }
195
196 if (ruby_port_ptr == NULL) {
197 ruby_port_ptr = ruby_port_map[cntrl];
198 }
199 }
200
201 assert(ruby_port_ptr != NULL);
202
203 for (int cntrl = 0; cntrl < m_abs_cntrl_vec.size(); cntrl++) {
204 if (ruby_port_map[cntrl] == NULL) {
205 ruby_port_map[cntrl] = ruby_port_ptr;
206 } else {
207 ruby_port_ptr = ruby_port_map[cntrl];
208 }
209
210 }
211
212 // Remove the old CacheRecorder if it's still hanging about.
213 if (m_cache_recorder != NULL) {
214 delete m_cache_recorder;
215 }
216
217 // Create the CacheRecorder and record the cache trace
218 m_cache_recorder = new CacheRecorder(uncompressed_trace, cache_trace_size,
219 ruby_port_map, block_size_bytes,
221}
222
223void
225{
226 m_cooldown_enabled = true;
227
228 // Make the trace so we know what to write back.
229 DPRINTF(RubyCacheTrace, "Recording Cache Trace\n");
231 for (int cntrl = 0; cntrl < m_abs_cntrl_vec.size(); cntrl++) {
232 m_abs_cntrl_vec[cntrl]->recordCacheTrace(cntrl, m_cache_recorder);
233 }
234 DPRINTF(RubyCacheTrace, "Cache Trace Complete\n");
235
236 // If there is no dirty block, we don't need to flush the cache
238 {
239 m_cooldown_enabled = false;
240 return;
241 }
242
243 // save the current tick value
244 Tick curtick_original = curTick();
245 DPRINTF(RubyCacheTrace, "Recording current tick %ld\n", curtick_original);
246
247 // Deschedule all prior events on the event queue, but record the tick they
248 // were scheduled at so they can be restored correctly later.
249 std::list<std::pair<Event*, Tick> > original_events;
250 while (!eventq->empty()) {
251 Event *curr_head = eventq->getHead();
252 if (curr_head->isAutoDelete()) {
253 DPRINTF(RubyCacheTrace, "Event %s auto-deletes when descheduled,"
254 " not recording\n", curr_head->name());
255 } else {
256 original_events.push_back(
257 std::make_pair(curr_head, curr_head->when()));
258 }
259 eventq->deschedule(curr_head);
260 }
261
262 // Schedule an event to start cache cooldown
263 DPRINTF(RubyCacheTrace, "Starting cache flush\n");
265 simulate();
266 DPRINTF(RubyCacheTrace, "Cache flush complete\n");
267
268 // Deschedule any events left on the event queue.
269 while (!eventq->empty()) {
271 }
272
273 // Restore curTick
274 setCurTick(curtick_original);
275
276 // Restore all events that were originally on the event queue. This is
277 // done after setting curTick back to its original value so that events do
278 // not seem to be scheduled in the past.
279 while (!original_events.empty()) {
280 std::pair<Event*, Tick> event = original_events.back();
281 eventq->schedule(event.first, event.second);
282 original_events.pop_back();
283 }
284
285 // No longer flushing back to memory.
286 m_cooldown_enabled = false;
287
288 // There are several issues with continuing simulation after calling
289 // memWriteback() at the moment, that stem from taking events off the
290 // queue, simulating again, and then putting them back on, whilst
291 // pretending that no time has passed. One is that some events will have
292 // been deleted, so can't be put back. Another is that any object
293 // recording the tick something happens may end up storing a tick in the
294 // future. A simple warning here alerts the user that things may not work
295 // as expected.
296 warn_once("Ruby memory writeback is experimental. Continuing simulation "
297 "afterwards may not always work as intended.");
298
299 // Keep the cache recorder around so that we can dump the trace if a
300 // checkpoint is immediately taken.
301}
302
303void
304RubySystem::writeCompressedTrace(uint8_t *raw_data, std::string filename,
305 uint64_t uncompressed_trace_size)
306{
307 // Create the checkpoint file for the memory
308 std::string thefile = CheckpointIn::dir() + "/" + filename.c_str();
309
310 int fd = creat(thefile.c_str(), 0664);
311 if (fd < 0) {
312 perror("creat");
313 fatal("Can't open memory trace file '%s'\n", filename);
314 }
315
316 gzFile compressedMemory = gzdopen(fd, "wb");
317 if (compressedMemory == NULL)
318 fatal("Insufficient memory to allocate compression state for %s\n",
319 filename);
320
321 if (gzwrite(compressedMemory, raw_data, uncompressed_trace_size) !=
322 uncompressed_trace_size) {
323 fatal("Write failed on memory trace file '%s'\n", filename);
324 }
325
326 if (gzclose(compressedMemory)) {
327 fatal("Close failed on memory trace file '%s'\n", filename);
328 }
329 delete[] raw_data;
330}
331
332void
334{
335 // Store the cache-block size, so we are able to restore on systems
336 // with a different cache-block size. CacheRecorder depends on the
337 // correct cache-block size upon unserializing.
338 uint64_t block_size_bytes = m_block_size_bytes;
339 SERIALIZE_SCALAR(block_size_bytes);
340
341 // Check that there's a valid trace to use. If not, then memory won't
342 // be up-to-date and the simulation will probably fail when restoring
343 // from the checkpoint.
344 if (m_cache_recorder == NULL) {
345 fatal("Call memWriteback() before serialize() to create"
346 "ruby trace");
347 }
348
349 // Aggregate the trace entries together into a single array
350 uint8_t *raw_data = new uint8_t[4096];
351 uint64_t cache_trace_size = m_cache_recorder->aggregateRecords(
352 &raw_data, 4096);
353 std::string cache_trace_file = name() + ".cache.gz";
354 writeCompressedTrace(raw_data, cache_trace_file, cache_trace_size);
355
356 SERIALIZE_SCALAR(cache_trace_file);
357 SERIALIZE_SCALAR(cache_trace_size);
358}
359
360void
362{
363 // Delete the cache recorder if it was created in memWriteback()
364 // to checkpoint the current cache state.
365 if (m_cache_recorder) {
366 delete m_cache_recorder;
367 m_cache_recorder = NULL;
368 }
369}
370
371void
372RubySystem::readCompressedTrace(std::string filename, uint8_t *&raw_data,
373 uint64_t &uncompressed_trace_size)
374{
375 // Read the trace file
376 gzFile compressedTrace;
377
378 // trace file
379 int fd = open(filename.c_str(), O_RDONLY);
380 if (fd < 0) {
381 perror("open");
382 fatal("Unable to open trace file %s", filename);
383 }
384
385 compressedTrace = gzdopen(fd, "rb");
386 if (compressedTrace == NULL) {
387 fatal("Insufficient memory to allocate compression state for %s\n",
388 filename);
389 }
390
391 raw_data = new uint8_t[uncompressed_trace_size];
392 if (gzread(compressedTrace, raw_data, uncompressed_trace_size) <
393 uncompressed_trace_size) {
394 fatal("Unable to read complete trace from file %s\n", filename);
395 }
396
397 if (gzclose(compressedTrace)) {
398 fatal("Failed to close cache trace file '%s'\n", filename);
399 }
400}
401
402void
404{
405 uint8_t *uncompressed_trace = NULL;
406
407 // This value should be set to the checkpoint-system's block-size.
408 // Optional, as checkpoints without it can be run if the
409 // checkpoint-system's block-size == current block-size.
410 uint64_t block_size_bytes = getBlockSizeBytes();
411 UNSERIALIZE_OPT_SCALAR(block_size_bytes);
412
413 std::string cache_trace_file;
414 uint64_t cache_trace_size = 0;
415
416 UNSERIALIZE_SCALAR(cache_trace_file);
417 UNSERIALIZE_SCALAR(cache_trace_size);
418 cache_trace_file = cp.getCptDir() + "/" + cache_trace_file;
419
420 readCompressedTrace(cache_trace_file, uncompressed_trace,
421 cache_trace_size);
422 m_warmup_enabled = true;
423
424 // Create the cache recorder that will hang around until startup.
425 makeCacheRecorder(uncompressed_trace, cache_trace_size, block_size_bytes);
426}
427
428void
433
434void
436{
437
438 // Ruby restores state from a checkpoint by resetting the clock to 0 and
439 // playing the requests that can possibly re-generate the cache state.
440 // The clock value is set to the actual checkpointed value once all the
441 // requests have been executed.
442 //
443 // This way of restoring state is pretty finicky. For example, if a
444 // Ruby component reads time before the state has been restored, it would
445 // cache this value and hence its clock would not be reset to 0, when
446 // Ruby resets the global clock. This can potentially result in a
447 // deadlock.
448 //
449 // The solution is that no Ruby component should read time before the
450 // simulation starts. And then one also needs to hope that the time
451 // Ruby finishes restoring the state is less than the time when the
452 // state was checkpointed.
453
454 if (m_warmup_enabled) {
455 DPRINTF(RubyCacheTrace, "Starting ruby cache warmup\n");
456 // save the current tick value
457 Tick curtick_original = curTick();
458 // save the event queue head
459 Event* eventq_head = eventq->replaceHead(NULL);
460 // save the exit event pointer
461 GlobalSimLoopExitEvent *original_simulate_limit_event = nullptr;
462 original_simulate_limit_event = simulate_limit_event;
463 // set curTick to 0 and reset Ruby System's clock
464 setCurTick(0);
465 resetClock();
466
467 // Schedule an event to start cache warmup
469 simulate();
470
471 delete m_cache_recorder;
472 m_cache_recorder = NULL;
473 m_warmup_enabled = false;
474
475 // Restore eventq head
476 eventq->replaceHead(eventq_head);
477 // Restore exit event pointer
478 simulate_limit_event = original_simulate_limit_event;
479 // Restore curTick and Ruby System's clock
480 setCurTick(curtick_original);
481 resetClock();
482 }
483
484 resetStats();
485}
486
487void
496
497void
499{
501 for (auto& network : m_networks) {
502 network->resetStats();
503 }
505}
506
507bool
509 if (protocolInfo->getPartialFuncReads()) {
510 return partialFunctionalRead(pkt);
511 } else {
512 return simpleFunctionalRead(pkt);
513 }
514}
515
516bool
518{
519 Addr address(pkt->getAddr());
520 Addr line_address = makeLineAddress(address, m_block_size_bits);
521
522 AccessPermission access_perm = AccessPermission_NotPresent;
523
524 DPRINTF(RubySystem, "Functional Read request for %#x\n", address);
525
526 unsigned int num_ro = 0;
527 unsigned int num_rw = 0;
528 unsigned int num_busy = 0;
529 unsigned int num_maybe_stale = 0;
530 unsigned int num_backing_store = 0;
531 unsigned int num_invalid = 0;
532
533 // Only send functional requests within the same network.
534 assert(requestorToNetwork.count(pkt->requestorId()));
535 int request_net_id = requestorToNetwork[pkt->requestorId()];
536 assert(netCntrls.count(request_net_id));
537
538 AbstractController *ctrl_ro = nullptr;
539 AbstractController *ctrl_rw = nullptr;
540 AbstractController *ctrl_ms = nullptr;
541 AbstractController *ctrl_backing_store = nullptr;
542
543 // In this loop we count the number of controllers that have the given
544 // address in read only, read write and busy states.
545 for (auto& cntrl : netCntrls[request_net_id]) {
546 access_perm = cntrl-> getAccessPermission(line_address);
547 if (access_perm == AccessPermission_Read_Only){
548 num_ro++;
549 if (ctrl_ro == nullptr) ctrl_ro = cntrl;
550 }
551 else if (access_perm == AccessPermission_Read_Write){
552 num_rw++;
553 if (ctrl_rw == nullptr) ctrl_rw = cntrl;
554 }
555 else if (access_perm == AccessPermission_Busy)
556 num_busy++;
557 else if (access_perm == AccessPermission_Maybe_Stale) {
558 int priority = cntrl->functionalReadPriority();
559 if (priority >= 0) {
560 if (ctrl_ms == nullptr) {
561 ctrl_ms = cntrl;
562 } else {
563 int current_priority = ctrl_ms->functionalReadPriority();
564 if (ctrl_ms == nullptr || priority < current_priority) {
565 ctrl_ms = cntrl;
566 } else if (priority == current_priority) {
567 warn("More than one Abstract Controller with "
568 "Maybe_Stale permission and same priority (%d) "
569 "for addr: %#x on cacheline: %#x.", priority,
570 address, line_address);
571 }
572 }
573 }
574 num_maybe_stale++;
575 } else if (access_perm == AccessPermission_Backing_Store) {
576 // See RubySlicc_Exports.sm for details, but Backing_Store is meant
577 // to represent blocks in memory *for Broadcast/Snooping protocols*,
578 // where memory has no idea whether it has an exclusive copy of data
579 // or not.
580 num_backing_store++;
581 if (ctrl_backing_store == nullptr)
582 ctrl_backing_store = cntrl;
583 }
584 else if (access_perm == AccessPermission_Invalid ||
585 access_perm == AccessPermission_NotPresent)
586 num_invalid++;
587 }
588
589 // This if case is meant to capture what happens in a Broadcast/Snoop
590 // protocol where the block does not exist in the cache hierarchy. You
591 // only want to read from the Backing_Store memory if there is no copy in
592 // the cache hierarchy, otherwise you want to try to read the RO or RW
593 // copies existing in the cache hierarchy (covered by the else statement).
594 // The reason is because the Backing_Store memory could easily be stale, if
595 // there are copies floating around the cache hierarchy, so you want to read
596 // it only if it's not in the cache hierarchy at all.
597 int num_controllers = netCntrls[request_net_id].size();
598 if (num_invalid == (num_controllers - 1) && num_backing_store == 1) {
600 "only copy in Backing_Store memory, read from it\n");
601 ctrl_backing_store->functionalRead(line_address, pkt);
602 return true;
603 } else if (num_ro > 0 || num_rw >= 1) {
604 if (num_rw > 1) {
605 // We iterate over the vector of abstract controllers, and return
606 // the first copy found. If we have more than one cache with block
607 // in writable permission, the first one found would be returned.
608 warn("More than one Abstract Controller with RW permission for "
609 "addr: %#x on cacheline: %#x.", address, line_address);
610 }
611 // In Broadcast/Snoop protocols, this covers if you know the block
612 // exists somewhere in the caching hierarchy, then you want to read any
613 // valid RO or RW block. In directory protocols, same thing, you want
614 // to read any valid readable copy of the block.
615 DPRINTF(RubySystem, "num_maybe_stale=%d, num_busy = %d, num_ro = %d, "
616 "num_rw = %d\n",
617 num_maybe_stale, num_busy, num_ro, num_rw);
618 // Use the copy from the controller with read/write permission (if
619 // any), otherwise use get the first read only found
620 if (ctrl_rw) {
621 ctrl_rw->functionalRead(line_address, pkt);
622 } else {
623 assert(ctrl_ro);
624 ctrl_ro->functionalRead(line_address, pkt);
625 }
626 return true;
627 } else if ((num_busy + num_maybe_stale) > 0) {
628 // No controller has a valid copy of the block, but a transient or
629 // stale state indicates a valid copy should be in transit in the
630 // network or in a message buffer waiting to be handled
631 DPRINTF(RubySystem, "Controllers functionalRead lookup "
632 "(num_maybe_stale=%d, num_busy = %d)\n",
633 num_maybe_stale, num_busy);
634 for (auto& cntrl : netCntrls[request_net_id]) {
635 if (cntrl->functionalReadBuffers(pkt))
636 return true;
637 }
638 DPRINTF(RubySystem, "Network functionalRead lookup "
639 "(num_maybe_stale=%d, num_busy = %d)\n",
640 num_maybe_stale, num_busy);
641 for (auto& network : m_networks) {
642 if (network->functionalRead(pkt))
643 return true;
644 }
645 if (ctrl_ms != nullptr) {
646 // No copy in transit or buffered indicates that a block marked
647 // as Maybe_Stale is actually up-to-date, just waiting an Ack or
648 // similar type of message which carries no data.
649 ctrl_ms->functionalRead(line_address, pkt);
650 return true;
651 }
652 }
653
654 return false;
655}
656
657bool
659{
660 Addr address(pkt->getAddr());
661 Addr line_address = makeLineAddress(address, m_block_size_bits);
662
663 DPRINTF(RubySystem, "Functional Read request for %#x\n", address);
664
668 AbstractController *ctrl_rw = nullptr;
669 AbstractController *ctrl_bs = nullptr;
670
671 // Build lists of controllers that have line
672 for (auto ctrl : m_abs_cntrl_vec) {
673 switch(ctrl->getAccessPermission(line_address)) {
674 case AccessPermission_Read_Only:
675 ctrl_ro.push_back(ctrl);
676 break;
677 case AccessPermission_Busy:
678 ctrl_busy.push_back(ctrl);
679 break;
680 case AccessPermission_Read_Write:
681 assert(ctrl_rw == nullptr);
682 ctrl_rw = ctrl;
683 break;
684 case AccessPermission_Backing_Store:
685 assert(ctrl_bs == nullptr);
686 ctrl_bs = ctrl;
687 break;
688 case AccessPermission_Backing_Store_Busy:
689 assert(ctrl_bs == nullptr);
690 ctrl_bs = ctrl;
691 ctrl_busy.push_back(ctrl);
692 break;
693 default:
694 ctrl_others.push_back(ctrl);
695 break;
696 }
697 }
698
699 DPRINTF(RubySystem, "num_ro=%d, num_busy=%d , has_rw=%d, "
700 "backing_store=%d\n",
701 ctrl_ro.size(), ctrl_busy.size(),
702 ctrl_rw != nullptr, ctrl_bs != nullptr);
703
704 // Issue functional reads to all controllers found in a stable state
705 // until we get a full copy of the line
706 WriteMask bytes;
708 if (ctrl_rw != nullptr) {
709 ctrl_rw->functionalRead(line_address, pkt, bytes);
710 // if a RW controllter has the full line that's all uptodate
711 if (bytes.isFull())
712 return true;
713 }
714
715 // Get data from RO and BS
716 for (auto ctrl : ctrl_ro)
717 ctrl->functionalRead(line_address, pkt, bytes);
718
719 if (ctrl_bs)
720 ctrl_bs->functionalRead(line_address, pkt, bytes);
721
722 // if there is any busy controller or bytes still not set, then a partial
723 // and/or dirty copy of the line might be in a message buffer or the
724 // network
725 if (!ctrl_busy.empty() || !bytes.isFull()) {
726 DPRINTF(RubySystem, "Reading from remaining controllers, "
727 "buffers and networks\n");
728 if (ctrl_rw != nullptr)
729 ctrl_rw->functionalReadBuffers(pkt, bytes);
730 for (auto ctrl : ctrl_ro)
731 ctrl->functionalReadBuffers(pkt, bytes);
732 if (ctrl_bs != nullptr)
733 ctrl_bs->functionalReadBuffers(pkt, bytes);
734 for (auto ctrl : ctrl_busy) {
735 ctrl->functionalRead(line_address, pkt, bytes);
736 ctrl->functionalReadBuffers(pkt, bytes);
737 }
738 for (auto& network : m_networks) {
739 network->functionalRead(pkt, bytes);
740 }
741 for (auto ctrl : ctrl_others) {
742 ctrl->functionalRead(line_address, pkt, bytes);
743 ctrl->functionalReadBuffers(pkt, bytes);
744 }
745 }
746 // we either got the full line or couldn't find anything at this point
747 panic_if(!(bytes.isFull() || bytes.isEmpty()),
748 "Inconsistent state on functional read for %#x %s\n",
749 address, bytes);
750
751 return bytes.isFull();
752}
753
754// The function searches through all the buffers that exist in different
755// cache, directory and memory controllers, and in the network components
756// and writes the data portion of those that hold the address specified
757// in the packet.
758bool
760{
761 Addr addr(pkt->getAddr());
763 AccessPermission access_perm = AccessPermission_NotPresent;
764
765 DPRINTF(RubySystem, "Functional Write request for %#x\n", addr);
766
767 [[maybe_unused]] uint32_t num_functional_writes = 0;
768
769 // Only send functional requests within the same network.
770 assert(requestorToNetwork.count(pkt->requestorId()));
771 int request_net_id = requestorToNetwork[pkt->requestorId()];
772 assert(netCntrls.count(request_net_id));
773
774 for (auto& cntrl : netCntrls[request_net_id]) {
775 num_functional_writes += cntrl->functionalWriteBuffers(pkt);
776
777 access_perm = cntrl->getAccessPermission(line_addr);
778 if (access_perm != AccessPermission_Invalid &&
779 access_perm != AccessPermission_NotPresent) {
780 num_functional_writes +=
781 cntrl->functionalWrite(line_addr, pkt);
782 }
783
784 // Also updates requests pending in any sequencer associated
785 // with the controller
786 if (cntrl->getCPUSequencer()) {
787 num_functional_writes +=
788 cntrl->getCPUSequencer()->functionalWrite(pkt);
789 }
790 if (cntrl->getDMASequencer()) {
791 num_functional_writes +=
792 cntrl->getDMASequencer()->functionalWrite(pkt);
793 }
794 }
795
796 for (auto& network : m_networks) {
797 num_functional_writes += network->functionalWrite(pkt);
798 }
799 DPRINTF(RubySystem, "Messages written = %u\n", num_functional_writes);
800
801 return true;
802}
803
804} // namespace ruby
805} // namespace gem5
#define DPRINTF(x,...)
Definition trace.hh:209
The ClockedObject class extends the SimObject with a clock and accessor functions to relate ticks to ...
ClockedObjectParams Params
Parameters of ClockedObject.
Cycles curCycle() const
Determine the current cycle, corresponding to a tick aligned to a clock edge.
void resetClock() const
Reset the object's clock using the current global tick value.
void setCurTick(Tick newVal)
Definition eventq.hh:1073
EventQueue * eventq
A pointer to this object's event queue.
Definition eventq.hh:984
Event * getHead() const
Definition eventq.hh:851
Event * replaceHead(Event *s)
function for replacing the head of the event queue, so that a different set of events can run without...
Definition eventq.cc:382
virtual std::string name() const
Definition named.hh:47
A Packet is used to encapsulate a transfer between two objects in the memory system (e....
Definition packet.hh:295
Addr getAddr() const
Definition packet.hh:807
RequestorID requestorId() const
Definition packet.hh:780
virtual bool functionalReadBuffers(PacketPtr &)=0
These functions are used by ruby system to read/write the data blocks that exist with in the controll...
virtual int functionalReadPriority()
Returns the priority used by functional reads when deciding from which controller to read a Maybe_Sta...
virtual void functionalRead(const Addr &addr, PacketPtr)
uint64_t aggregateRecords(uint8_t **data, uint64_t size)
uint64_t getNumRecords() const
void enqueueNextFetchRequest()
Function for fetching warming up the memory and the caches.
void enqueueNextFlushRequest()
Function for flushing the memory contents of the caches to the main memory.
void enqueueRubyEvent(Tick tick)
bool functionalWrite(Packet *ptr)
void unserialize(CheckpointIn &cp) override
Unserialize an object.
void init() override
init() is called after all C++ SimObjects have been created and all ports are connected.
std::vector< std::map< uint32_t, AbstractController * > > m_abstract_controls
void registerMachineID(const MachineID &mach_id, Network *network)
std::unique_ptr< ProtocolInfo > protocolInfo
std::unordered_map< RequestorID, unsigned > requestorToNetwork
static void writeCompressedTrace(uint8_t *raw_data, std::string file, uint64_t uncompressed_trace_size)
RubySystem(const Params &p)
Definition RubySystem.cc:72
void registerNetwork(Network *)
Definition RubySystem.cc:94
void startup() override
startup() is the final initialization call before simulation.
std::unordered_map< MachineID, unsigned > machineToNetwork
uint32_t getBlockSizeBytes()
Definition RubySystem.hh:73
static void readCompressedTrace(std::string filename, uint8_t *&raw_data, uint64_t &uncompressed_trace_size)
bool simpleFunctionalRead(PacketPtr pkt)
CacheRecorder * m_cache_recorder
std::vector< std::unique_ptr< Network > > m_networks
void drainResume() override
Resume execution after a successful drain.
std::vector< AbstractController * > m_abs_cntrl_vec
void resetStats() override
Callback to reset stats.
bool partialFunctionalRead(PacketPtr pkt)
memory::SimpleMemory * m_phys_mem
std::unordered_map< unsigned, std::vector< AbstractController * > > netCntrls
bool functionalRead(Packet *ptr)
void makeCacheRecorder(uint8_t *uncompressed_trace, uint64_t cache_trace_size, uint64_t block_size_bytes)
void serialize(CheckpointOut &cp) const override
Serialize an object.
void registerAbstractController(AbstractController *, std::unique_ptr< ProtocolInfo >)
void memWriteback() override
Write back dirty buffers to memory using functional writes.
bool isFull() const
Definition WriteMask.hh:177
bool isEmpty() const
Definition WriteMask.hh:165
void setBlockSize(int size)
Definition WriteMask.hh:83
STL list class.
Definition stl.hh:51
STL pair class.
Definition stl.hh:58
STL vector class.
Definition stl.hh:37
static constexpr std::enable_if_t< std::is_integral_v< T >, int > floorLog2(T x)
Definition intmath.hh:59
static constexpr bool isPowerOf2(const T &n)
Definition intmath.hh:98
void schedule(Event *event, Tick when, bool global=false)
Schedule the given event on this queue.
Definition eventq.hh:757
void deschedule(Event *event)
Deschedule the specified event.
Definition eventq.hh:790
bool isAutoDelete() const
The function returns true if the object is automatically deleted after the event is processed.
Definition eventq.hh:494
virtual const std::string name() const
Definition eventq.cc:84
bool empty() const
Returns true if no events are queued.
Definition eventq.hh:891
Tick when() const
Get the time that the event is scheduled.
Definition eventq.hh:501
#define fatal_if(cond,...)
Conditional fatal macro that checks the supplied condition and only causes a fatal error if the condi...
Definition logging.hh:236
#define fatal(...)
This implements a cprintf based fatal() function.
Definition logging.hh:200
#define panic_if(cond,...)
Conditional panic macro that checks the supplied condition and only panics if the condition is true a...
Definition logging.hh:214
#define UNSERIALIZE_OPT_SCALAR(scalar)
Definition serialize.hh:582
const std::string getCptDir()
Definition serialize.hh:85
static std::string dir()
Get the current checkout directory name.
Definition serialize.cc:157
const Params & params() const
virtual void resetStats()
Callback to reset stats.
Definition group.cc:86
#define warn(...)
Definition logging.hh:256
#define warn_once(...)
Definition logging.hh:260
Bitfield< 14, 12 > fd
Definition types.hh:150
Bitfield< 33 > id
Bitfield< 3, 0 > priority
Bitfield< 10, 5 > event
Bitfield< 0 > p
Bitfield< 3 > addr
Definition types.hh:84
Addr makeLineAddress(Addr addr, int cacheLineBits)
Definition Address.cc:61
std::string MachineIDToString(MachineID machine)
Definition MachineID.hh:73
void registerDumpCallback(const std::function< void()> &callback)
Register a callback that should be called whenever statistics are about to be dumped.
Copyright (c) 2024 Arm Limited All rights reserved.
Definition binary32.hh:36
GlobalSimLoopExitEvent * simulate_limit_event
Definition simulate.cc:64
Tick curTick()
The universal simulation clock.
Definition cur_tick.hh:46
std::ostream CheckpointOut
Definition serialize.hh:66
uint64_t Addr
Address type This will probably be moved somewhere else in the near future.
Definition types.hh:147
uint64_t Tick
Tick count type.
Definition types.hh:58
uint16_t RequestorID
Definition request.hh:95
GlobalSimLoopExitEvent * simulate(Tick num_cycles)
Definition simulate.cc:189
#define UNSERIALIZE_SCALAR(scalar)
Definition serialize.hh:575
#define SERIALIZE_SCALAR(scalar)
Definition serialize.hh:568
SimpleMemory declaration.
Declaration of Statistics objects.

Generated on Mon Jan 13 2025 04:28:41 for gem5 by doxygen 1.9.8