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.
35 #include "mem/ruby/common/Global.hh"
36 #include "mem/ruby/config/RubyConfig.hh"
37 #include "mem/ruby/system/StoreBuffer.hh"
38 #include "mem/ruby/slicc_interface/AbstractChip.hh"
39 #include "mem/ruby/system/System.hh"
40 #include "mem/ruby/common/Driver.hh"
41 #include "mem/gems_common/Vector.hh"
42 #include "mem/ruby/eventqueue/RubyEventQueue.hh"
43 #include "mem/ruby/profiler/AddressProfiler.hh"
44 #include "mem/ruby/system/Sequencer.hh"
45 #include "mem/ruby/common/SubBlock.hh"
46 #include "mem/ruby/profiler/Profiler.hh"
48 // *** Begin Helper class ***
49 struct StoreBufferEntry
{
50 StoreBufferEntry() {} // So we can allocate a vector of StoreBufferEntries
51 StoreBufferEntry(const SubBlock
& block
, CacheRequestType type
, const Address
& pc
, AccessModeType access_mode
, int size
, int thread
) : m_subblock(block
) {
54 m_access_mode
= access_mode
;
57 m_time
= g_eventQueue_ptr
->getTime();
60 void print(ostream
& out
) const
62 out
<< "[StoreBufferEntry: "
63 << "SubBlock: " << m_subblock
64 << ", Type: " << m_type
66 << ", AccessMode: " << m_access_mode
67 << ", Size: " << m_size
68 << ", Thread: " << m_thread
69 << ", Time: " << m_time
74 CacheRequestType m_type
;
76 AccessModeType m_access_mode
;
83 ostream
& operator<<(ostream
& out
, const StoreBufferEntry
& obj
)
90 // *** End Helper class ***
92 const int MAX_ENTRIES
= 128;
94 static void inc_index(int& index
)
97 if (index
>= MAX_ENTRIES
) {
102 StoreBuffer::StoreBuffer(AbstractChip
* chip_ptr
, int version
) :
105 m_chip_ptr
= chip_ptr
;
107 m_queue_ptr
= new Vector
<StoreBufferEntry
>(MAX_ENTRIES
);
108 m_queue_ptr
->setSize(MAX_ENTRIES
);
110 m_seen_atomic
= false;
114 m_deadlock_check_scheduled
= false;
117 StoreBuffer::~StoreBuffer()
122 // Used only to check for deadlock
123 void StoreBuffer::wakeup()
125 // Check for deadlock of any of the requests
126 Time current_time
= g_eventQueue_ptr
->getTime();
128 int queue_pointer
= m_head
;
129 for (int i
=0; i
<m_size
; i
++) {
130 if (current_time
- (getEntry(queue_pointer
).m_time
) >= g_DEADLOCK_THRESHOLD
) {
131 WARN_EXPR(getEntry(queue_pointer
));
132 WARN_EXPR(m_chip_ptr
->getID());
133 WARN_EXPR(current_time
);
134 ERROR_MSG("Possible Deadlock detected");
136 inc_index(queue_pointer
);
139 if (m_size
> 0) { // If there are still outstanding requests, keep checking
140 g_eventQueue_ptr
->scheduleEvent(this, g_DEADLOCK_THRESHOLD
);
142 m_deadlock_check_scheduled
= false;
146 void StoreBuffer::printConfig(ostream
& out
)
148 out
<< "Store buffer entries: " << MAX_ENTRIES
<< " (Only valid if TSO is enabled)" << endl
;
151 // Handle an incoming store request, this method is responsible for
152 // calling hitCallback as needed
153 void StoreBuffer::insertStore(const CacheMsg
& request
)
155 Address addr
= request
.getAddress();
156 CacheRequestType type
= request
.getType();
157 Address pc
= request
.getProgramCounter();
158 AccessModeType access_mode
= request
.getAccessMode();
159 int size
= request
.getSize();
160 int threadID
= request
.getThreadID();
162 DEBUG_MSG(STOREBUFFER_COMP
, MedPrio
, "insertStore");
163 DEBUG_EXPR(STOREBUFFER_COMP
, MedPrio
, g_eventQueue_ptr
->getTime());
164 assert((type
== CacheRequestType_ST
) || (type
== CacheRequestType_ATOMIC
));
167 // See if we should schedule a deadlock check
168 if (m_deadlock_check_scheduled
== false) {
169 g_eventQueue_ptr
->scheduleEvent(this, g_DEADLOCK_THRESHOLD
);
170 m_deadlock_check_scheduled
= true;
173 // Perform the hit-callback for the store
174 SubBlock
subblock(addr
, size
);
175 if(type
== CacheRequestType_ST
) {
176 g_system_ptr
->getDriver()->hitCallback(m_chip_ptr
->getID(), subblock
, type
, threadID
);
177 assert(subblock
.getSize() != 0);
179 // wait to perform the hitCallback until later for Atomics
182 // Perform possible pre-fetch
184 CacheMsg new_request
= request
;
185 new_request
.getPrefetch() = PrefetchBit_Yes
;
186 m_chip_ptr
->getSequencer(m_version
)->makeRequest(new_request
);
189 // Update the StoreCache
190 m_store_cache
.add(subblock
);
193 StoreBufferEntry
entry(subblock
, type
, pc
, access_mode
, size
, threadID
); // FIXME
196 if(type
== CacheRequestType_ATOMIC
) {
197 m_seen_atomic
= true;
200 processHeadOfQueue();
203 void StoreBuffer::callBack(const Address
& addr
, DataBlock
& data
)
205 DEBUG_MSG(STOREBUFFER_COMP
, MedPrio
, "callBack");
206 DEBUG_EXPR(STOREBUFFER_COMP
, MedPrio
, g_eventQueue_ptr
->getTime());
208 assert(m_pending
== true);
209 assert(line_address(addr
) == addr
);
210 assert(line_address(m_pending_address
) == addr
);
211 assert(line_address(peek().m_subblock
.getAddress()) == addr
);
212 CacheRequestType type
= peek().m_type
;
213 int threadID
= peek().m_thread
;
214 assert((type
== CacheRequestType_ST
) || (type
== CacheRequestType_ATOMIC
));
217 // If oldest entry was ATOMIC, perform the callback
218 if(type
== CacheRequestType_ST
) {
219 // We already performed the call back for the store at insert time
221 // We waited to perform the hitCallback until now for Atomics
222 peek().m_subblock
.mergeFrom(data
); // copy the correct bytes from DataBlock into the SubBlock for the Load part of the atomic Load/Store
223 g_system_ptr
->getDriver()->hitCallback(m_chip_ptr
->getID(), peek().m_subblock
, type
, threadID
);
224 m_seen_atomic
= false;
226 /// FIXME - record the time spent in the store buffer - split out ST vs ATOMIC
228 assert(peek().m_subblock
.getSize() != 0);
230 // Apply the head entry to the datablock
231 peek().m_subblock
.mergeTo(data
); // For both the Store and Atomic cases
233 // Update the StoreCache
234 m_store_cache
.remove(peek().m_subblock
);
236 // Dequeue the entry from the store buffer
240 assert(m_store_cache
.isEmpty());
243 if(type
== CacheRequestType_ATOMIC
) {
247 // See if we can remove any more entries
248 processHeadOfQueue();
251 void StoreBuffer::processHeadOfQueue()
253 if(!isEmpty() && !m_pending
) {
254 StoreBufferEntry
& entry
= peek();
255 assert(m_pending
== false);
257 m_pending_address
= entry
.m_subblock
.getAddress();
258 CacheMsg
request(entry
.m_subblock
.getAddress(), entry
.m_subblock
.getAddress(), entry
.m_type
, entry
.m_pc
, entry
.m_access_mode
, entry
.m_size
, PrefetchBit_No
, 0, Address(0), entry
.m_thread
);
259 m_chip_ptr
->getSequencer(m_version
)->doRequest(request
);
263 bool StoreBuffer::isReady() const
265 return ((m_size
< MAX_ENTRIES
) && (!m_seen_atomic
));
268 // Queue implementation methods
270 StoreBufferEntry
& StoreBuffer::peek()
272 return getEntry(m_head
);
275 void StoreBuffer::dequeue()
282 void StoreBuffer::enqueue(const StoreBufferEntry
& entry
)
284 // assert(isReady());
285 (*m_queue_ptr
)[m_tail
] = entry
;
287 g_system_ptr
->getProfiler()->storeBuffer(m_size
, m_store_cache
.size());
291 StoreBufferEntry
& StoreBuffer::getEntry(int index
)
293 return (*m_queue_ptr
)[index
];
296 void StoreBuffer::print(ostream
& out
) const
298 out
<< "[StoreBuffer]";