gem5 v24.0.0.0
Loading...
Searching...
No Matches
MessageBuffer.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-2008 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 <cassert>
44
45#include "base/cprintf.hh"
46#include "base/logging.hh"
47#include "base/random.hh"
48#include "base/stl_helpers.hh"
49#include "debug/RubyQueue.hh"
51
52namespace gem5
53{
54
55namespace ruby
56{
57
58using stl_helpers::operator<<;
59
61 : SimObject(p), m_stall_map_size(0), m_max_size(p.buffer_size),
62 m_max_dequeue_rate(p.max_dequeue_rate), m_dequeues_this_cy(0),
63 m_time_last_time_size_checked(0),
64 m_time_last_time_enqueue(0), m_time_last_time_pop(0),
65 m_last_arrival_time(0), m_last_message_strict_fifo_bypassed(false),
66 m_strict_fifo(p.ordered),
67 m_randomization(p.randomization),
68 m_allow_zero_latency(p.allow_zero_latency),
69 m_routing_priority(p.routing_priority),
70 ADD_STAT(m_not_avail_count, statistics::units::Count::get(),
71 "Number of times this buffer did not have N slots available"),
72 ADD_STAT(m_msg_count, statistics::units::Count::get(),
73 "Number of messages passed the buffer"),
74 ADD_STAT(m_buf_msgs, statistics::units::Rate<
75 statistics::units::Count, statistics::units::Tick>::get(),
76 "Average number of messages in buffer"),
77 ADD_STAT(m_stall_time, statistics::units::Tick::get(),
78 "Total number of ticks messages were stalled in this buffer"),
79 ADD_STAT(m_stall_count, statistics::units::Count::get(),
80 "Number of times messages were stalled"),
81 ADD_STAT(m_avg_stall_time, statistics::units::Rate<
82 statistics::units::Tick, statistics::units::Count>::get(),
83 "Average stall ticks per message"),
84 ADD_STAT(m_occupancy, statistics::units::Rate<
85 statistics::units::Ratio, statistics::units::Tick>::get(),
86 "Average occupancy of buffer capacity")
87{
88 m_msg_counter = 0;
89 m_consumer = NULL;
95
96 m_stall_msg_map.clear();
98 m_vnet_id = 0;
99
100 m_buf_msgs = 0;
101 m_stall_time = 0;
102
103 m_dequeue_callback = nullptr;
104
105 // stats
108
111
114
117
120
123
126
127 if (m_max_size > 0) {
129 } else {
130 m_occupancy = 0;
131 }
132
134}
135
136unsigned int
146
147bool
148MessageBuffer::areNSlotsAvailable(unsigned int n, Tick current_time)
149{
150
151 // fast path when message buffers have infinite size
152 if (m_max_size == 0) {
153 return true;
154 }
155
156 // determine the correct size for the current cycle
157 // pop operations shouldn't effect the network's visible size
158 // until schd cycle, but enqueue operations effect the visible
159 // size immediately
160 unsigned int current_size = 0;
161 unsigned int current_stall_size = 0;
162
163 if (m_time_last_time_pop < current_time) {
164 // no pops this cycle - heap and stall queue size is correct
165 current_size = m_prio_heap.size();
166 current_stall_size = m_stall_map_size;
167 } else {
168 if (m_time_last_time_enqueue < current_time) {
169 // no enqueues this cycle - m_size_at_cycle_start is correct
170 current_size = m_size_at_cycle_start;
171 } else {
172 // both pops and enqueues occured this cycle - add new
173 // enqueued msgs to m_size_at_cycle_start
175 }
176
177 // Stall queue size at start is considered
178 current_stall_size = m_stalled_at_cycle_start;
179 }
180
181 // now compare the new size with our max size
182 if (current_size + current_stall_size + n <= m_max_size) {
183 return true;
184 } else {
185 DPRINTF(RubyQueue, "n: %d, current_size: %d, heap size: %d, "
186 "m_max_size: %d\n",
187 n, current_size + current_stall_size,
188 m_prio_heap.size(), m_max_size);
190 return false;
191 }
192}
193
194const Message*
196{
197 DPRINTF(RubyQueue, "Peeking at head of queue.\n");
198 const Message* msg_ptr = m_prio_heap.front().get();
199 assert(msg_ptr);
200
201 DPRINTF(RubyQueue, "Message: %s\n", (*msg_ptr));
202 return msg_ptr;
203}
204
205// FIXME - move me somewhere else
206Tick
208{
209 Tick time = 1;
210 time += random_mt.random(0, 3); // [0...3]
211 if (random_mt.random(0, 7) == 0) { // 1 in 8 chance
212 time += 100 + random_mt.random(1, 15); // 100 + [1...15]
213 }
214 return time;
215}
216
217void
218MessageBuffer::enqueue(MsgPtr message, Tick current_time, Tick delta,
219 bool bypassStrictFIFO)
220{
221 // record current time incase we have a pop that also adjusts my size
222 if (m_time_last_time_enqueue < current_time) {
223 m_msgs_this_cycle = 0; // first msg this cycle
224 m_time_last_time_enqueue = current_time;
225 }
226
229
230 // Calculate the arrival time of the message, that is, the first
231 // cycle the message can be dequeued.
232 panic_if((delta == 0) && !m_allow_zero_latency,
233 "Delta equals zero and allow_zero_latency is false during enqueue");
234 Tick arrival_time = 0;
235
236 // random delays are inserted if the RubySystem level randomization flag
237 // is turned on and this buffer allows it
238 if ((m_randomization == MessageRandomization::disabled) ||
239 ((m_randomization == MessageRandomization::ruby_system) &&
241 // No randomization
242 arrival_time = current_time + delta;
243 } else {
244 // Randomization - ignore delta
245 if (m_strict_fifo) {
246 if (m_last_arrival_time < current_time) {
247 m_last_arrival_time = current_time;
248 }
249 arrival_time = m_last_arrival_time + random_time();
250 } else {
251 arrival_time = current_time + random_time();
252 }
253 }
254
255 // Check the arrival time
256 assert(arrival_time >= current_time);
257 if (m_strict_fifo &&
258 !(bypassStrictFIFO || m_last_message_strict_fifo_bypassed)) {
259 if (arrival_time < m_last_arrival_time) {
260 panic("FIFO ordering violated: %s name: %s current time: %d "
261 "delta: %d arrival_time: %d last arrival_time: %d\n",
262 *this, name(), current_time, delta, arrival_time,
264 }
265 }
266
267 // If running a cache trace, don't worry about the last arrival checks
269 m_last_arrival_time = arrival_time;
270 }
271
272 m_last_message_strict_fifo_bypassed = bypassStrictFIFO;
273
274 // compute the delay cycles and set enqueue time
275 Message* msg_ptr = message.get();
276 assert(msg_ptr != NULL);
277
278 assert(current_time >= msg_ptr->getLastEnqueueTime() &&
279 "ensure we aren't dequeued early");
280
281 msg_ptr->updateDelayedTicks(current_time);
282 msg_ptr->setLastEnqueueTime(arrival_time);
284
285 // Insert the message into the priority heap
286 m_prio_heap.push_back(message);
287 push_heap(m_prio_heap.begin(), m_prio_heap.end(), std::greater<MsgPtr>());
288 // Increment the number of messages statistic
289 m_buf_msgs++;
290
291 assert((m_max_size == 0) ||
292 ((m_prio_heap.size() + m_stall_map_size) <= m_max_size));
293
294 DPRINTF(RubyQueue, "Enqueue arrival_time: %lld, Message: %s\n",
295 arrival_time, *(message.get()));
296
297 // Schedule the wakeup
298 assert(m_consumer != NULL);
299 m_consumer->scheduleEventAbsolute(arrival_time);
301}
302
303Tick
304MessageBuffer::dequeue(Tick current_time, bool decrement_messages)
305{
306 DPRINTF(RubyQueue, "Popping\n");
307 assert(isReady(current_time));
308
309 // get MsgPtr of the message about to be dequeued
310 MsgPtr message = m_prio_heap.front();
311
312 // get the delay cycles
313 message->updateDelayedTicks(current_time);
314 Tick delay = message->getDelayedTicks();
315
316 // record previous size and time so the current buffer size isn't
317 // adjusted until schd cycle
318 if (m_time_last_time_pop < current_time) {
321 m_time_last_time_pop = current_time;
323 }
325
326 pop_heap(m_prio_heap.begin(), m_prio_heap.end(), std::greater<MsgPtr>());
327 m_prio_heap.pop_back();
328 if (decrement_messages) {
329 // Record how much time is passed since the message was enqueued
330 m_stall_time += curTick() - message->getLastEnqueueTime();
331 m_msg_count++;
332
333 // If the message will be removed from the queue, decrement the
334 // number of message in the queue.
335 m_buf_msgs--;
336 }
337
338 // if a dequeue callback was requested, call it now
339 if (m_dequeue_callback) {
341 }
342
343 return delay;
344}
345
346void
347MessageBuffer::registerDequeueCallback(std::function<void()> callback)
348{
349 m_dequeue_callback = callback;
350}
351
352void
357
358void
370
371void
372MessageBuffer::recycle(Tick current_time, Tick recycle_latency)
373{
374 DPRINTF(RubyQueue, "Recycling.\n");
375 assert(isReady(current_time));
376 MsgPtr node = m_prio_heap.front();
377 pop_heap(m_prio_heap.begin(), m_prio_heap.end(), std::greater<MsgPtr>());
378
379 Tick future_time = current_time + recycle_latency;
380 node->setLastEnqueueTime(future_time);
381
382 m_prio_heap.back() = node;
383 push_heap(m_prio_heap.begin(), m_prio_heap.end(), std::greater<MsgPtr>());
384 m_consumer->scheduleEventAbsolute(future_time);
385}
386
387void
389{
390 while (!lt.empty()) {
391 MsgPtr m = lt.front();
392 assert(m->getLastEnqueueTime() <= schdTick);
393
394 m_prio_heap.push_back(m);
395 push_heap(m_prio_heap.begin(), m_prio_heap.end(),
396 std::greater<MsgPtr>());
397
399
400 DPRINTF(RubyQueue, "Requeue arrival_time: %lld, Message: %s\n",
401 schdTick, *(m.get()));
402
403 lt.pop_front();
404 }
405}
406
407void
409{
410 DPRINTF(RubyQueue, "ReanalyzeMessages %#x\n", addr);
411 assert(m_stall_msg_map.count(addr) > 0);
412
413 //
414 // Put all stalled messages associated with this address back on the
415 // prio heap. The reanalyzeList call will make sure the consumer is
416 // scheduled for the current cycle so that the previously stalled messages
417 // will be observed before any younger messages that may arrive this cycle
418 //
420 assert(m_stall_map_size >= 0);
421 reanalyzeList(m_stall_msg_map[addr], current_time);
422 m_stall_msg_map.erase(addr);
423}
424
425void
427{
428 DPRINTF(RubyQueue, "ReanalyzeAllMessages\n");
429
430 //
431 // Put all stalled messages associated with this address back on the
432 // prio heap. The reanalyzeList call will make sure the consumer is
433 // scheduled for the current cycle so that the previously stalled messages
434 // will be observed before any younger messages that may arrive this cycle.
435 //
436 for (StallMsgMapType::iterator map_iter = m_stall_msg_map.begin();
437 map_iter != m_stall_msg_map.end(); ++map_iter) {
438 m_stall_map_size -= map_iter->second.size();
439 assert(m_stall_map_size >= 0);
440 reanalyzeList(map_iter->second, current_time);
441 }
442 m_stall_msg_map.clear();
443}
444
445void
447{
448 DPRINTF(RubyQueue, "Stalling due to %#x\n", addr);
449 assert(isReady(current_time));
450 assert(getOffset(addr) == 0);
451 MsgPtr message = m_prio_heap.front();
452
453 // Since the message will just be moved to stall map, indicate that the
454 // buffer should not decrement the m_buf_msgs statistic
455 dequeue(current_time, false);
456
457 //
458 // Note: no event is scheduled to analyze the map at a later time.
459 // Instead the controller is responsible to call reanalyzeMessages when
460 // these addresses change state.
461 //
462 (m_stall_msg_map[addr]).push_back(message);
465}
466
467bool
469{
470 return (m_stall_msg_map.count(addr) != 0);
471}
472
473void
475{
476 DPRINTF(RubyQueue, "Deferring enqueueing message: %s, Address %#x\n",
477 *(message.get()), addr);
478 (m_deferred_msg_map[addr]).push_back(message);
479}
480
481void
483{
484 assert(!isDeferredMsgMapEmpty(addr));
486 assert(msg_vec.size() > 0);
487
488 // enqueue all deferred messages associated with this address
489 for (MsgPtr m : msg_vec) {
490 enqueue(m, curTime, delay);
491 }
492
493 msg_vec.clear();
495}
496
497bool
502
503void
504MessageBuffer::print(std::ostream& out) const
505{
506 ccprintf(out, "[MessageBuffer: ");
507 if (m_consumer != NULL) {
508 ccprintf(out, " consumer-yes ");
509 }
510
512 std::sort_heap(copy.begin(), copy.end(), std::greater<MsgPtr>());
513 ccprintf(out, "%s] %s", copy, name());
514}
515
516bool
517MessageBuffer::isReady(Tick current_time) const
518{
519 assert(m_time_last_time_pop <= current_time);
520 bool can_dequeue = (m_max_dequeue_rate == 0) ||
521 (m_time_last_time_pop < current_time) ||
523 bool is_ready = (m_prio_heap.size() > 0) &&
524 (m_prio_heap.front()->getLastEnqueueTime() <= current_time);
525 if (!can_dequeue && is_ready) {
526 // Make sure the Consumer executes next cycle to dequeue the ready msg
528 }
529 return can_dequeue && is_ready;
530}
531
532Tick
534{
535 if (m_prio_heap.empty())
536 return MaxTick;
537 else
538 return m_prio_heap.front()->getLastEnqueueTime();
539}
540
541uint32_t
543{
544 DPRINTF(RubyQueue, "functional %s for %#x\n",
545 is_read ? "read" : "write", pkt->getAddr());
546
547 uint32_t num_functional_accesses = 0;
548
549 // Check the priority heap and write any messages that may
550 // correspond to the address in the packet.
551 for (unsigned int i = 0; i < m_prio_heap.size(); ++i) {
552 Message *msg = m_prio_heap[i].get();
553 if (is_read && !mask && msg->functionalRead(pkt))
554 return 1;
555 else if (is_read && mask && msg->functionalRead(pkt, *mask))
556 num_functional_accesses++;
557 else if (!is_read && msg->functionalWrite(pkt))
558 num_functional_accesses++;
559 }
560
561 // Check the stall queue and write any messages that may
562 // correspond to the address in the packet.
563 for (StallMsgMapType::iterator map_iter = m_stall_msg_map.begin();
564 map_iter != m_stall_msg_map.end();
565 ++map_iter) {
566
567 for (std::list<MsgPtr>::iterator it = (map_iter->second).begin();
568 it != (map_iter->second).end(); ++it) {
569
570 Message *msg = (*it).get();
571 if (is_read && !mask && msg->functionalRead(pkt))
572 return 1;
573 else if (is_read && mask && msg->functionalRead(pkt, *mask))
574 num_functional_accesses++;
575 else if (!is_read && msg->functionalWrite(pkt))
576 num_functional_accesses++;
577 }
578 }
579
580 return num_functional_accesses;
581}
582
583} // namespace ruby
584} // namespace gem5
#define DPRINTF(x,...)
Definition trace.hh:210
Cycles is a wrapper class for representing cycle counts, i.e.
Definition types.hh:79
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
Abstract superclass for simulation objects.
void scheduleEventAbsolute(Tick timeAbs)
Definition Consumer.cc:63
void scheduleEvent(Cycles timeDelta)
Definition Consumer.cc:56
virtual void storeEventInfo(int info)
Definition Consumer.hh:73
statistics::Scalar m_stall_count
bool isDeferredMsgMapEmpty(Addr addr) const
void recycle(Tick current_time, Tick recycle_latency)
MessageBuffer(const Params &p)
bool areNSlotsAvailable(unsigned int n, Tick curTime)
bool isReady(Tick current_time) const
const Message * peek() const
Function for extracting the message at the head of the message queue.
unsigned int m_size_last_time_size_checked
void enqueueDeferredMessages(Addr addr, Tick curTime, Tick delay)
void enqueue(MsgPtr message, Tick curTime, Tick delta, bool bypassStrictFIFO=false)
const unsigned int m_max_dequeue_rate
When != 0, isReady returns false once m_max_dequeue_rate messages have been dequeued in the same cycl...
MessageBufferParams Params
const unsigned int m_max_size
The maximum capacity.
void reanalyzeAllMessages(Tick current_time)
unsigned int m_stalled_at_cycle_start
statistics::Formula m_avg_stall_time
Consumer * m_consumer
Consumer to signal a wakeup(), can be NULL.
Tick dequeue(Tick current_time, bool decrement_messages=true)
Updates the delay cycles of the message at the head of the queue, removes it from the queue and retur...
statistics::Average m_buf_msgs
statistics::Scalar m_stall_time
bool hasStalledMsg(Addr addr) const
std::vector< MsgPtr > m_prio_heap
void deferEnqueueingMessage(Addr addr, MsgPtr message)
void registerDequeueCallback(std::function< void()> callback)
StallMsgMapType m_stall_msg_map
A map from line addresses to lists of stalled messages for that line.
void stallMessage(Addr addr, Tick current_time)
statistics::Scalar m_msg_count
statistics::Formula m_occupancy
void print(std::ostream &out) const
void reanalyzeMessages(Addr addr, Tick current_time)
const MessageRandomization m_randomization
void reanalyzeList(std::list< MsgPtr > &, Tick)
unsigned int getSize(Tick curTime)
int m_stall_map_size
Current size of the stall map.
statistics::Scalar m_not_avail_count
std::function< void()> m_dequeue_callback
uint32_t functionalAccess(Packet *pkt, bool is_read, WriteMask *mask)
DeferredMsgMapType m_deferred_msg_map
virtual bool functionalRead(Packet *pkt)
The two functions below are used for reading / writing the message functionally.
Definition Message.hh:90
Tick getLastEnqueueTime() const
Definition Message.hh:107
virtual bool functionalWrite(Packet *pkt)
Definition Message.hh:94
void updateDelayedTicks(Tick curTime)
Update the delay this message has experienced so far.
Definition Message.hh:98
void setMsgCounter(uint64_t c)
Definition Message.hh:110
void setLastEnqueueTime(const Tick &time)
Definition Message.hh:106
static bool getWarmupEnabled()
Definition RubySystem.hh:75
static int getRandomization()
Definition RubySystem.hh:71
Derived & flags(Flags _flags)
Set the flags and marks this stat to print at the end of simulation.
STL list class.
Definition stl.hh:51
STL vector class.
Definition stl.hh:37
#define ADD_STAT(n,...)
Convenience macro to add a stat to a statistics group.
Definition group.hh:75
Random random_mt
Definition random.cc:99
std::enable_if_t< std::is_integral_v< T >, T > random()
Use the SFINAE idiom to choose an implementation based on whether the type is integral or floating po...
Definition random.hh:90
#define panic(...)
This implements a cprintf based panic() function.
Definition logging.hh:188
#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
Bitfield< 31 > n
Bitfield< 3, 0 > mask
Definition pcstate.hh:63
Bitfield< 7 > i
Definition misc_types.hh:67
Bitfield< 0 > m
Bitfield< 0 > p
Bitfield< 31 > lt
Definition misc.hh:55
Bitfield< 3 > addr
Definition types.hh:84
std::shared_ptr< Message > MsgPtr
Definition Message.hh:60
Tick random_time()
Addr getOffset(Addr addr)
Definition Address.cc:54
const FlagsType nonan
Don't print if this is NAN.
Definition info.hh:69
const FlagsType nozero
Don't print if this is zero.
Definition info.hh:67
Copyright (c) 2024 - Pranith Kumar Copyright (c) 2020 Inria All rights reserved.
Definition binary32.hh:36
Tick curTick()
The universal simulation clock.
Definition cur_tick.hh:46
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
const Tick MaxTick
Definition types.hh:60
void ccprintf(cp::Print &print)
Definition cprintf.hh:130

Generated on Tue Jun 18 2024 16:24:05 for gem5 by doxygen 1.11.0