4d67e55a541e14fa2220a7cc54d6b69fa52b1c37
[gem5.git] / src / mem / ruby / system / StoreBuffer.cc
1
2 /*
3 * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
4 * All rights reserved.
5 *
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.
16 *
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.
28 */
29
30 /*
31 * $Id$
32 *
33 */
34
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"
47
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) {
52 m_type = type;
53 m_pc = pc;
54 m_access_mode = access_mode;
55 m_size = size;
56 m_thread = thread;
57 m_time = g_eventQueue_ptr->getTime();
58 }
59
60 void print(ostream& out) const
61 {
62 out << "[StoreBufferEntry: "
63 << "SubBlock: " << m_subblock
64 << ", Type: " << m_type
65 << ", PC: " << m_pc
66 << ", AccessMode: " << m_access_mode
67 << ", Size: " << m_size
68 << ", Thread: " << m_thread
69 << ", Time: " << m_time
70 << "]";
71 }
72
73 SubBlock m_subblock;
74 CacheRequestType m_type;
75 Address m_pc;
76 AccessModeType m_access_mode;
77 int m_size;
78 int m_thread;
79 Time m_time;
80 };
81
82 extern inline
83 ostream& operator<<(ostream& out, const StoreBufferEntry& obj)
84 {
85 obj.print(out);
86 out << flush;
87 return out;
88 }
89
90 // *** End Helper class ***
91
92 const int MAX_ENTRIES = 128;
93
94 static void inc_index(int& index)
95 {
96 index++;
97 if (index >= MAX_ENTRIES) {
98 index = 0;
99 }
100 }
101
102 StoreBuffer::StoreBuffer(AbstractChip* chip_ptr, int version) :
103 m_store_cache()
104 {
105 m_chip_ptr = chip_ptr;
106 m_version = version;
107 m_queue_ptr = new Vector<StoreBufferEntry>(MAX_ENTRIES);
108 m_queue_ptr->setSize(MAX_ENTRIES);
109 m_pending = false;
110 m_seen_atomic = false;
111 m_head = 0;
112 m_tail = 0;
113 m_size = 0;
114 m_deadlock_check_scheduled = false;
115 }
116
117 StoreBuffer::~StoreBuffer()
118 {
119 delete m_queue_ptr;
120 }
121
122 // Used only to check for deadlock
123 void StoreBuffer::wakeup()
124 {
125 // Check for deadlock of any of the requests
126 Time current_time = g_eventQueue_ptr->getTime();
127
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");
135 }
136 inc_index(queue_pointer);
137 }
138
139 if (m_size > 0) { // If there are still outstanding requests, keep checking
140 g_eventQueue_ptr->scheduleEvent(this, g_DEADLOCK_THRESHOLD);
141 } else {
142 m_deadlock_check_scheduled = false;
143 }
144 }
145
146 void StoreBuffer::printConfig(ostream& out)
147 {
148 out << "Store buffer entries: " << MAX_ENTRIES << " (Only valid if TSO is enabled)" << endl;
149 }
150
151 // Handle an incoming store request, this method is responsible for
152 // calling hitCallback as needed
153 void StoreBuffer::insertStore(const CacheMsg& request)
154 {
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();
161
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));
165 assert(isReady());
166
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;
171 }
172
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);
178 } else {
179 // wait to perform the hitCallback until later for Atomics
180 }
181
182 // Perform possible pre-fetch
183 if(!isEmpty()) {
184 CacheMsg new_request = request;
185 new_request.getPrefetch() = PrefetchBit_Yes;
186 m_chip_ptr->getSequencer(m_version)->makeRequest(new_request);
187 }
188
189 // Update the StoreCache
190 m_store_cache.add(subblock);
191
192 // Enqueue the entry
193 StoreBufferEntry entry(subblock, type, pc, access_mode, size, threadID); // FIXME
194 enqueue(entry);
195
196 if(type == CacheRequestType_ATOMIC) {
197 m_seen_atomic = true;
198 }
199
200 processHeadOfQueue();
201 }
202
203 void StoreBuffer::callBack(const Address& addr, DataBlock& data)
204 {
205 DEBUG_MSG(STOREBUFFER_COMP, MedPrio, "callBack");
206 DEBUG_EXPR(STOREBUFFER_COMP, MedPrio, g_eventQueue_ptr->getTime());
207 assert(!isEmpty());
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));
215 m_pending = false;
216
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
220 } else {
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;
225
226 /// FIXME - record the time spent in the store buffer - split out ST vs ATOMIC
227 }
228 assert(peek().m_subblock.getSize() != 0);
229
230 // Apply the head entry to the datablock
231 peek().m_subblock.mergeTo(data); // For both the Store and Atomic cases
232
233 // Update the StoreCache
234 m_store_cache.remove(peek().m_subblock);
235
236 // Dequeue the entry from the store buffer
237 dequeue();
238
239 if (isEmpty()) {
240 assert(m_store_cache.isEmpty());
241 }
242
243 if(type == CacheRequestType_ATOMIC) {
244 assert(isEmpty());
245 }
246
247 // See if we can remove any more entries
248 processHeadOfQueue();
249 }
250
251 void StoreBuffer::processHeadOfQueue()
252 {
253 if(!isEmpty() && !m_pending) {
254 StoreBufferEntry& entry = peek();
255 assert(m_pending == false);
256 m_pending = true;
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);
260 }
261 }
262
263 bool StoreBuffer::isReady() const
264 {
265 return ((m_size < MAX_ENTRIES) && (!m_seen_atomic));
266 }
267
268 // Queue implementation methods
269
270 StoreBufferEntry& StoreBuffer::peek()
271 {
272 return getEntry(m_head);
273 }
274
275 void StoreBuffer::dequeue()
276 {
277 assert(m_size > 0);
278 m_size--;
279 inc_index(m_head);
280 }
281
282 void StoreBuffer::enqueue(const StoreBufferEntry& entry)
283 {
284 // assert(isReady());
285 (*m_queue_ptr)[m_tail] = entry;
286 m_size++;
287 g_system_ptr->getProfiler()->storeBuffer(m_size, m_store_cache.size());
288 inc_index(m_tail);
289 }
290
291 StoreBufferEntry& StoreBuffer::getEntry(int index)
292 {
293 return (*m_queue_ptr)[index];
294 }
295
296 void StoreBuffer::print(ostream& out) const
297 {
298 out << "[StoreBuffer]";
299 }
300