gem5 [DEVELOP-FOR-25.0]
Loading...
Searching...
No Matches
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),
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
237 if (m_cache_recorder->getNumRecords() == 0)
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()) {
270 eventq->deschedule(eventq->getHead());
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
489{
490 if (getWarmupEnabled()) {
491 m_cache_recorder->enqueueNextFetchRequest();
492 } else if (getCooldownEnabled()) {
493 m_cache_recorder->enqueueNextFlushRequest();
494 }
495}
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
ClockedObject(const ClockedObjectParams &p)
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
virtual std::string name() const
Definition named.hh:60
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)
void enqueueRubyEvent(Tick tick)
bool functionalWrite(Packet *ptr)
void unserialize(CheckpointIn &cp) override
Unserialize an object.
const bool m_access_backing_store
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:179
bool isEmpty() const
Definition WriteMask.hh:167
void setBlockSize(int size)
Definition WriteMask.hh:84
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
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
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:268
#define fatal(...)
This implements a cprintf based fatal() function.
Definition logging.hh:232
#define panic_if(cond,...)
Conditional panic macro that checks the supplied condition and only panics if the condition is true a...
Definition logging.hh:246
#define UNSERIALIZE_OPT_SCALAR(scalar)
Definition serialize.hh:582
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:288
#define warn_once(...)
Definition logging.hh:292
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:66
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
Packet * PacketPtr
GlobalSimLoopExitEvent * simulate(Tick num_cycles)
Definition simulate.cc:191
#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 May 26 2025 09:19:12 for gem5 by doxygen 1.13.2