3 * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met: redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer;
10 * redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution;
13 * neither the name of the copyright holders nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 #include "mem/ruby/buffers/MessageBuffer.hh"
35 #include "mem/ruby/config/RubyConfig.hh"
37 MessageBuffer::MessageBuffer()
40 m_consumer_ptr
= NULL
;
41 m_ordering_set
= false;
45 m_last_arrival_time
= 0;
46 m_randomization
= true;
47 m_size_last_time_size_checked
= 0;
48 m_time_last_time_size_checked
= 0;
49 m_time_last_time_enqueue
= 0;
50 m_time_last_time_pop
= 0;
51 m_size_at_cycle_start
= 0;
52 m_msgs_this_cycle
= 0;
53 m_not_avail_count
= 0;
57 MessageBuffer::MessageBuffer(const Chip
* chip_ptr
) // The chip_ptr is ignored, but could be used for extra debugging
60 m_consumer_ptr
= NULL
;
61 m_ordering_set
= false;
65 m_last_arrival_time
= 0;
66 m_randomization
= true;
67 m_size_last_time_size_checked
= 0;
68 m_time_last_time_size_checked
= 0;
69 m_time_last_time_enqueue
= 0;
70 m_time_last_time_pop
= 0;
71 m_size_at_cycle_start
= 0;
72 m_msgs_this_cycle
= 0;
73 m_not_avail_count
= 0;
77 int MessageBuffer::getSize()
79 if(m_time_last_time_size_checked
== g_eventQueue_ptr
->getTime()){
80 return m_size_last_time_size_checked
;
82 m_time_last_time_size_checked
= g_eventQueue_ptr
->getTime();
83 m_size_last_time_size_checked
= m_size
;
88 bool MessageBuffer::areNSlotsAvailable(int n
)
91 // fast path when message buffers have infinite size
92 if(m_max_size
== -1) {
96 // determine my correct size for the current cycle
97 // pop operations shouldn't effect the network's visible size until next cycle,
98 // but enqueue operations effect the visible size immediately
99 int current_size
= max(m_size_at_cycle_start
, m_size
);
100 if (m_time_last_time_pop
< g_eventQueue_ptr
->getTime()) { // no pops this cycle - m_size is correct
101 current_size
= m_size
;
103 if (m_time_last_time_enqueue
< g_eventQueue_ptr
->getTime()) { // no enqueues this cycle - m_size_at_cycle_start is correct
104 current_size
= m_size_at_cycle_start
;
105 } else { // both pops and enqueues occured this cycle - add new enqueued msgs to m_size_at_cycle_start
106 current_size
= m_size_at_cycle_start
+m_msgs_this_cycle
;
110 // now compare the new size with our max size
111 if(current_size
+n
<= m_max_size
){
114 DEBUG_MSG(QUEUE_COMP
,MedPrio
,n
);
115 DEBUG_MSG(QUEUE_COMP
,MedPrio
,current_size
);
116 DEBUG_MSG(QUEUE_COMP
,MedPrio
,m_size
);
117 DEBUG_MSG(QUEUE_COMP
,MedPrio
,m_max_size
);
123 const MsgPtr
MessageBuffer::getMsgPtrCopy() const
128 temp_msg
= *(m_prio_heap
.peekMin().m_msgptr
.ref());
129 assert(temp_msg
.ref() != NULL
);
133 const Message
* MessageBuffer::peekAtHeadOfQueue() const
135 const Message
* msg_ptr
;
136 DEBUG_NEWLINE(QUEUE_COMP
,MedPrio
);
138 DEBUG_MSG(QUEUE_COMP
,MedPrio
,"Peeking at head of queue " + m_name
+ " time: "
139 + int_to_string(g_eventQueue_ptr
->getTime()) + ".");
142 msg_ptr
= m_prio_heap
.peekMin().m_msgptr
.ref();
143 assert(msg_ptr
!= NULL
);
145 DEBUG_EXPR(QUEUE_COMP
,MedPrio
,*msg_ptr
);
146 DEBUG_NEWLINE(QUEUE_COMP
,MedPrio
);
150 // FIXME - move me somewhere else
154 time
+= random() & 0x3; // [0...3]
155 if ((random() & 0x7) == 0) { // 1 in 8 chance
156 time
+= 100 + (random() % 0xf); // 100 + [1...15]
161 void MessageBuffer::enqueue(const MsgPtr
& message
, Time delta
)
163 DEBUG_NEWLINE(QUEUE_COMP
,HighPrio
);
164 DEBUG_MSG(QUEUE_COMP
,HighPrio
,"enqueue " + m_name
+ " time: "
165 + int_to_string(g_eventQueue_ptr
->getTime()) + ".");
166 DEBUG_EXPR(QUEUE_COMP
,MedPrio
,message
);
167 DEBUG_NEWLINE(QUEUE_COMP
,HighPrio
);
172 // record current time incase we have a pop that also adjusts my size
173 if (m_time_last_time_enqueue
< g_eventQueue_ptr
->getTime()) {
174 m_msgs_this_cycle
= 0; // first msg this cycle
175 m_time_last_time_enqueue
= g_eventQueue_ptr
->getTime();
179 // ASSERT(m_max_size == -1 || m_size <= m_max_size + 1);
180 // the plus one is a kluge because of a SLICC issue
182 if (!m_ordering_set
) {
185 ERROR_MSG("Ordering property of this queue has not been set");
188 // Calculate the arrival time of the message, that is, the first
189 // cycle the message can be dequeued.
191 Time current_time
= g_eventQueue_ptr
->getTime();
192 Time arrival_time
= 0;
193 if (!RANDOMIZATION
|| (m_randomization
== false)) {
195 arrival_time
= current_time
+ delta
;
198 // Randomization - ignore delta
200 if (m_last_arrival_time
< current_time
) {
201 m_last_arrival_time
= current_time
;
203 arrival_time
= m_last_arrival_time
+ random_time();
205 arrival_time
= current_time
+ random_time();
209 // Check the arrival time
210 assert(arrival_time
> current_time
);
212 if (arrival_time
>= m_last_arrival_time
) {
217 WARN_EXPR(current_time
);
219 WARN_EXPR(arrival_time
);
220 WARN_EXPR(m_last_arrival_time
);
221 ERROR_MSG("FIFO ordering violated");
224 m_last_arrival_time
= arrival_time
;
226 // compute the delay cycles and set enqueue time
227 Message
* msg_ptr
= NULL
;
228 msg_ptr
= message
.mod_ref();
229 assert(msg_ptr
!= NULL
);
230 assert(g_eventQueue_ptr
->getTime() >= msg_ptr
->getLastEnqueueTime()); // ensure we aren't dequeued early
231 msg_ptr
->setDelayedCycles((g_eventQueue_ptr
->getTime() - msg_ptr
->getLastEnqueueTime())+msg_ptr
->getDelayedCycles());
232 msg_ptr
->setLastEnqueueTime(arrival_time
);
234 // Insert the message into the priority heap
235 MessageBufferNode
thisNode(arrival_time
, m_msg_counter
, message
);
236 m_prio_heap
.insert(thisNode
);
238 DEBUG_NEWLINE(QUEUE_COMP
,HighPrio
);
239 DEBUG_MSG(QUEUE_COMP
,HighPrio
,"enqueue " + m_name
240 + " with arrival_time " + int_to_string(arrival_time
)
241 + " cur_time: " + int_to_string(g_eventQueue_ptr
->getTime()) + ".");
242 DEBUG_EXPR(QUEUE_COMP
,MedPrio
,message
);
243 DEBUG_NEWLINE(QUEUE_COMP
,HighPrio
);
245 // Schedule the wakeup
246 if (m_consumer_ptr
!= NULL
) {
247 g_eventQueue_ptr
->scheduleEventAbsolute(m_consumer_ptr
, arrival_time
);
251 ERROR_MSG("No consumer");
255 int MessageBuffer::dequeue_getDelayCycles(MsgPtr
& message
)
257 int delay_cycles
= -1; // null value
261 // get the delay cycles
262 delay_cycles
= setAndReturnDelayCycles(message
);
264 assert(delay_cycles
>= 0);
268 void MessageBuffer::dequeue(MsgPtr
& message
)
270 DEBUG_MSG(QUEUE_COMP
,MedPrio
,"dequeue from " + m_name
);
271 message
= m_prio_heap
.peekMin().m_msgptr
;
274 DEBUG_EXPR(QUEUE_COMP
,MedPrio
,message
);
277 int MessageBuffer::dequeue_getDelayCycles()
279 int delay_cycles
= -1; // null value
281 // get MsgPtr of the message about to be dequeued
282 MsgPtr message
= m_prio_heap
.peekMin().m_msgptr
;
284 // get the delay cycles
285 delay_cycles
= setAndReturnDelayCycles(message
);
289 assert(delay_cycles
>= 0);
293 void MessageBuffer::pop()
295 DEBUG_MSG(QUEUE_COMP
,MedPrio
,"pop from " + m_name
);
297 Time ready_time
= m_prio_heap
.extractMin().m_time
;
298 // record previous size and time so the current buffer size isn't adjusted until next cycle
299 if (m_time_last_time_pop
< g_eventQueue_ptr
->getTime()) {
300 m_size_at_cycle_start
= m_size
;
301 m_time_last_time_pop
= g_eventQueue_ptr
->getTime();
306 void MessageBuffer::clear()
308 while(m_prio_heap
.size() > 0){
309 m_prio_heap
.extractMin();
312 ASSERT(m_prio_heap
.size() == 0);
316 m_time_last_time_enqueue
= 0;
317 m_time_last_time_pop
= 0;
318 m_size_at_cycle_start
= 0;
319 m_msgs_this_cycle
= 0;
322 void MessageBuffer::recycle()
324 // const int RECYCLE_LATENCY = 3;
325 DEBUG_MSG(QUEUE_COMP
,MedPrio
,"recycling " + m_name
);
327 MessageBufferNode node
= m_prio_heap
.extractMin();
328 node
.m_time
= g_eventQueue_ptr
->getTime() + RECYCLE_LATENCY
;
329 m_prio_heap
.insert(node
);
330 g_eventQueue_ptr
->scheduleEventAbsolute(m_consumer_ptr
, g_eventQueue_ptr
->getTime() + RECYCLE_LATENCY
);
333 int MessageBuffer::setAndReturnDelayCycles(MsgPtr
& message
)
335 int delay_cycles
= -1; // null value
337 // get the delay cycles of the message at the top of the queue
338 Message
* msg_ptr
= message
.ref();
340 // this function should only be called on dequeue
341 // ensure the msg hasn't been enqueued
342 assert(msg_ptr
->getLastEnqueueTime() <= g_eventQueue_ptr
->getTime());
343 msg_ptr
->setDelayedCycles((g_eventQueue_ptr
->getTime() - msg_ptr
->getLastEnqueueTime())+msg_ptr
->getDelayedCycles());
344 delay_cycles
= msg_ptr
->getDelayedCycles();
346 assert(delay_cycles
>= 0);
350 void MessageBuffer::print(ostream
& out
) const
352 out
<< "[MessageBuffer: ";
353 if (m_consumer_ptr
!= NULL
) {
354 out
<< " consumer-yes ";
356 out
<< m_prio_heap
<< "] " << m_name
<< endl
;
359 void MessageBuffer::printStats(ostream
& out
)
361 out
<< "MessageBuffer: " << m_name
<< " stats - msgs:" << m_msg_counter
<< " full:" << m_not_avail_count
<< endl
;