Ruby: Add support for functional accesses
[gem5.git] / src / mem / ruby / system / RubyPort.cc
1 /*
2 * Copyright (c) 2009 Advanced Micro Devices, Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met: redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer;
9 * redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution;
12 * neither the name of the copyright holders nor the names of its
13 * contributors may be used to endorse or promote products derived from
14 * this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include "config/the_isa.hh"
30 #if THE_ISA == X86_ISA
31 #include "arch/x86/insts/microldstop.hh"
32 #endif // X86_ISA
33 #include "cpu/testers/rubytest/RubyTester.hh"
34 #include "debug/Ruby.hh"
35 #include "mem/protocol/AccessPermission.hh"
36 #include "mem/ruby/slicc_interface/AbstractController.hh"
37 #include "mem/ruby/system/RubyPort.hh"
38 #include "mem/physical.hh"
39
40 RubyPort::RubyPort(const Params *p)
41 : MemObject(p)
42 {
43 m_version = p->version;
44 assert(m_version != -1);
45
46 physmem = p->physmem;
47
48 m_controller = NULL;
49 m_mandatory_q_ptr = NULL;
50
51 m_request_cnt = 0;
52 pio_port = NULL;
53 physMemPort = NULL;
54
55 m_usingRubyTester = p->using_ruby_tester;
56 access_phys_mem = p->access_phys_mem;
57
58 ruby_system = p->ruby_system;
59 }
60
61 void
62 RubyPort::init()
63 {
64 assert(m_controller != NULL);
65 m_mandatory_q_ptr = m_controller->getMandatoryQueue();
66 }
67
68 Port *
69 RubyPort::getPort(const std::string &if_name, int idx)
70 {
71 if (if_name == "port") {
72 return new M5Port(csprintf("%s-port%d", name(), idx), this,
73 ruby_system, access_phys_mem);
74 }
75
76 if (if_name == "pio_port") {
77 // ensure there is only one pio port
78 assert(pio_port == NULL);
79
80 pio_port = new PioPort(csprintf("%s-pio-port%d", name(), idx), this);
81
82 return pio_port;
83 }
84
85 if (if_name == "physMemPort") {
86 // RubyPort should only have one port to physical memory
87 assert (physMemPort == NULL);
88
89 physMemPort = new M5Port(csprintf("%s-physMemPort", name()), this,
90 ruby_system, access_phys_mem);
91
92 return physMemPort;
93 }
94
95 if (if_name == "functional") {
96 // Calls for the functional port only want to access
97 // functional memory. Therefore, directly pass these calls
98 // ports to physmem.
99 assert(physmem != NULL);
100 return physmem->getPort(if_name, idx);
101 }
102
103 return NULL;
104 }
105
106 RubyPort::PioPort::PioPort(const std::string &_name,
107 RubyPort *_port)
108 : SimpleTimingPort(_name, _port)
109 {
110 DPRINTF(RubyPort, "creating port to ruby sequencer to cpu %s\n", _name);
111 ruby_port = _port;
112 }
113
114 RubyPort::M5Port::M5Port(const std::string &_name, RubyPort *_port,
115 RubySystem *_system, bool _access_phys_mem)
116 : SimpleTimingPort(_name, _port)
117 {
118 DPRINTF(RubyPort, "creating port from ruby sequcner to cpu %s\n", _name);
119 ruby_port = _port;
120 ruby_system = _system;
121 _onRetryList = false;
122 access_phys_mem = _access_phys_mem;
123 }
124
125 Tick
126 RubyPort::PioPort::recvAtomic(PacketPtr pkt)
127 {
128 panic("RubyPort::PioPort::recvAtomic() not implemented!\n");
129 return 0;
130 }
131
132 Tick
133 RubyPort::M5Port::recvAtomic(PacketPtr pkt)
134 {
135 panic("RubyPort::M5Port::recvAtomic() not implemented!\n");
136 return 0;
137 }
138
139
140 bool
141 RubyPort::PioPort::recvTiming(PacketPtr pkt)
142 {
143 // In FS mode, ruby memory will receive pio responses from devices
144 // and it must forward these responses back to the particular CPU.
145 DPRINTF(RubyPort, "Pio response for address %#x\n", pkt->getAddr());
146
147 assert(pkt->isResponse());
148
149 // First we must retrieve the request port from the sender State
150 RubyPort::SenderState *senderState =
151 safe_cast<RubyPort::SenderState *>(pkt->senderState);
152 M5Port *port = senderState->port;
153 assert(port != NULL);
154
155 // pop the sender state from the packet
156 pkt->senderState = senderState->saved;
157 delete senderState;
158
159 port->sendTiming(pkt);
160
161 return true;
162 }
163
164 bool
165 RubyPort::M5Port::recvTiming(PacketPtr pkt)
166 {
167 DPRINTF(RubyPort,
168 "Timing access caught for address %#x\n", pkt->getAddr());
169
170 //dsm: based on SimpleTimingPort::recvTiming(pkt);
171
172 // The received packets should only be M5 requests, which should never
173 // get nacked. There used to be code to hanldle nacks here, but
174 // I'm pretty sure it didn't work correctly with the drain code,
175 // so that would need to be fixed if we ever added it back.
176 assert(pkt->isRequest());
177
178 if (pkt->memInhibitAsserted()) {
179 warn("memInhibitAsserted???");
180 // snooper will supply based on copy of packet
181 // still target's responsibility to delete packet
182 delete pkt;
183 return true;
184 }
185
186 // Save the port in the sender state object to be used later to
187 // route the response
188 pkt->senderState = new SenderState(this, pkt->senderState);
189
190 // Check for pio requests and directly send them to the dedicated
191 // pio port.
192 if (!isPhysMemAddress(pkt->getAddr())) {
193 assert(ruby_port->pio_port != NULL);
194 DPRINTF(RubyPort,
195 "Request for address 0x%#x is assumed to be a pio request\n",
196 pkt->getAddr());
197
198 return ruby_port->pio_port->sendTiming(pkt);
199 }
200
201 // For DMA and CPU requests, translate them to ruby requests before
202 // sending them to our assigned ruby port.
203 RubyRequestType type = RubyRequestType_NULL;
204
205 // If valid, copy the pc to the ruby request
206 Addr pc = 0;
207 if (pkt->req->hasPC()) {
208 pc = pkt->req->getPC();
209 }
210
211 if (pkt->isLLSC()) {
212 if (pkt->isWrite()) {
213 DPRINTF(RubyPort, "Issuing SC\n");
214 type = RubyRequestType_Store_Conditional;
215 } else {
216 DPRINTF(RubyPort, "Issuing LL\n");
217 assert(pkt->isRead());
218 type = RubyRequestType_Load_Linked;
219 }
220 } else if (pkt->req->isLocked()) {
221 if (pkt->isWrite()) {
222 DPRINTF(RubyPort, "Issuing Locked RMW Write\n");
223 type = RubyRequestType_Locked_RMW_Write;
224 } else {
225 DPRINTF(RubyPort, "Issuing Locked RMW Read\n");
226 assert(pkt->isRead());
227 type = RubyRequestType_Locked_RMW_Read;
228 }
229 } else {
230 if (pkt->isRead()) {
231 if (pkt->req->isInstFetch()) {
232 type = RubyRequestType_IFETCH;
233 } else {
234 #if THE_ISA == X86_ISA
235 uint32_t flags = pkt->req->getFlags();
236 bool storeCheck = flags &
237 (TheISA::StoreCheck << TheISA::FlagShift);
238 #else
239 bool storeCheck = false;
240 #endif // X86_ISA
241 if (storeCheck) {
242 type = RubyRequestType_RMW_Read;
243 } else {
244 type = RubyRequestType_LD;
245 }
246 }
247 } else if (pkt->isWrite()) {
248 //
249 // Note: M5 packets do not differentiate ST from RMW_Write
250 //
251 type = RubyRequestType_ST;
252 } else if (pkt->isFlush()) {
253 type = RubyRequestType_FLUSH;
254 } else {
255 panic("Unsupported ruby packet type\n");
256 }
257 }
258
259 RubyRequest ruby_request(pkt->getAddr(), pkt->getPtr<uint8_t>(true),
260 pkt->getSize(), pc, type,
261 RubyAccessMode_Supervisor, pkt);
262
263 assert(ruby_request.m_PhysicalAddress.getOffset() + ruby_request.m_Size <=
264 RubySystem::getBlockSizeBytes());
265
266 // Submit the ruby request
267 RequestStatus requestStatus = ruby_port->makeRequest(ruby_request);
268
269 // If the request successfully issued then we should return true.
270 // Otherwise, we need to delete the senderStatus we just created and return
271 // false.
272 if (requestStatus == RequestStatus_Issued) {
273 DPRINTF(RubyPort, "Request %#x issued\n", pkt->getAddr());
274 return true;
275 }
276
277 //
278 // Unless one is using the ruby tester, record the stalled M5 port for
279 // later retry when the sequencer becomes free.
280 //
281 if (!ruby_port->m_usingRubyTester) {
282 ruby_port->addToRetryList(this);
283 }
284
285 DPRINTF(RubyPort,
286 "Request for address %#x did not issue because %s\n",
287 pkt->getAddr(), RequestStatus_to_string(requestStatus));
288
289 SenderState* senderState = safe_cast<SenderState*>(pkt->senderState);
290 pkt->senderState = senderState->saved;
291 delete senderState;
292 return false;
293 }
294
295 bool
296 RubyPort::M5Port::doFunctionalRead(PacketPtr pkt)
297 {
298 Address address(pkt->getAddr());
299 Address line_address(address);
300 line_address.makeLineAddress();
301
302 AccessPermission accessPerm = AccessPermission_NotPresent;
303 int num_controllers = ruby_system->m_abs_cntrl_vec.size();
304
305 // In this loop, we try to figure which controller has a read only or
306 // a read write copy of the given address. Any valid copy would suffice
307 // for a functional read.
308
309 DPRINTF(RubyPort, "Functional Read request for %s\n",address);
310 for(int i = 0;i < num_controllers;++i)
311 {
312 accessPerm = ruby_system->m_abs_cntrl_vec[i]
313 ->getAccessPermission(line_address);
314 if(accessPerm == AccessPermission_Read_Only ||
315 accessPerm == AccessPermission_Read_Write)
316 {
317 unsigned startByte = address.getAddress() - line_address.getAddress();
318
319 uint8* data = pkt->getPtr<uint8_t>(true);
320 unsigned int size_in_bytes = pkt->getSize();
321 DataBlock& block = ruby_system->m_abs_cntrl_vec[i]
322 ->getDataBlock(line_address);
323
324 DPRINTF(RubyPort, "reading from %s block %s\n",
325 ruby_system->m_abs_cntrl_vec[i]->name(), block);
326 for (unsigned i = 0; i < size_in_bytes; ++i)
327 {
328 data[i] = block.getByte(i + startByte);
329 }
330 return true;
331 }
332 }
333 return false;
334 }
335
336 bool
337 RubyPort::M5Port::doFunctionalWrite(PacketPtr pkt)
338 {
339 Address addr(pkt->getAddr());
340 Address line_addr = line_address(addr);
341 AccessPermission accessPerm = AccessPermission_NotPresent;
342 int num_controllers = ruby_system->m_abs_cntrl_vec.size();
343
344 DPRINTF(RubyPort, "Functional Write request for %s\n",addr);
345
346 unsigned int num_ro = 0;
347 unsigned int num_rw = 0;
348 unsigned int num_busy = 0;
349
350 // In this loop we count the number of controllers that have the given
351 // address in read only, read write and busy states.
352 for(int i = 0;i < num_controllers;++i)
353 {
354 accessPerm = ruby_system->m_abs_cntrl_vec[i]->
355 getAccessPermission(line_addr);
356 if(accessPerm == AccessPermission_Read_Only) num_ro++;
357 else if(accessPerm == AccessPermission_Read_Write) num_rw++;
358 else if(accessPerm == AccessPermission_Busy) num_busy++;
359 }
360
361 // If the number of read write copies is more than 1, then there is bug in
362 // coherence protocol. Otherwise, if all copies are in stable states, i.e.
363 // num_busy == 0, we update all the copies. If there is at least one copy
364 // in busy state, then we check if there is read write copy. If yes, then
365 // also we let the access go through.
366
367 DPRINTF(RubyPort, "num_busy = %d, num_ro = %d, num_rw = %d\n",
368 num_busy, num_ro, num_rw);
369 assert(num_rw <= 1);
370 if((num_busy == 0 && num_ro > 0) || num_rw == 1)
371 {
372 uint8* data = pkt->getPtr<uint8_t>(true);
373 unsigned int size_in_bytes = pkt->getSize();
374 unsigned startByte = addr.getAddress() - line_addr.getAddress();
375
376 for(int i = 0; i < num_controllers;++i)
377 {
378 accessPerm = ruby_system->m_abs_cntrl_vec[i]->
379 getAccessPermission(line_addr);
380 if(accessPerm == AccessPermission_Read_Only ||
381 accessPerm == AccessPermission_Read_Write||
382 accessPerm == AccessPermission_Maybe_Stale)
383 {
384 DataBlock& block = ruby_system->m_abs_cntrl_vec[i]
385 ->getDataBlock(line_addr);
386
387 DPRINTF(RubyPort, "%s\n",block);
388 for (unsigned i = 0; i < size_in_bytes; ++i)
389 {
390 block.setByte(i + startByte, data[i]);
391 }
392 DPRINTF(RubyPort, "%s\n",block);
393 }
394 }
395 return true;
396 }
397 return false;
398 }
399
400 void
401 RubyPort::M5Port::recvFunctional(PacketPtr pkt)
402 {
403 DPRINTF(RubyPort, "Functional access caught for address %#x\n",
404 pkt->getAddr());
405
406 // Check for pio requests and directly send them to the dedicated
407 // pio port.
408 if (!isPhysMemAddress(pkt->getAddr())) {
409 assert(ruby_port->pio_port != NULL);
410 DPRINTF(RubyPort, "Request for address 0x%#x is a pio request\n",
411 pkt->getAddr());
412 panic("RubyPort::PioPort::recvFunctional() not implemented!\n");
413 }
414
415 assert(pkt->getAddr() + pkt->getSize() <=
416 line_address(Address(pkt->getAddr())).getAddress() +
417 RubySystem::getBlockSizeBytes());
418
419 bool accessSucceeded = false;
420 bool needsResponse = pkt->needsResponse();
421
422 // Do the functional access on ruby memory
423 if (pkt->isRead()) {
424 accessSucceeded = doFunctionalRead(pkt);
425 } else if (pkt->isWrite()) {
426 accessSucceeded = doFunctionalWrite(pkt);
427 } else {
428 panic("RubyPort: unsupported functional command %s\n",
429 pkt->cmdString());
430 }
431
432 // Unless the requester explicitly said otherwise, generate an error if
433 // the functional request failed
434 if (!accessSucceeded && !pkt->suppressFuncError()) {
435 fatal("Ruby functional %s failed for address %#x\n",
436 pkt->isWrite() ? "write" : "read", pkt->getAddr());
437 }
438
439 if (access_phys_mem) {
440 // The attached physmem contains the official version of data.
441 // The following command performs the real functional access.
442 // This line should be removed once Ruby supplies the official version
443 // of data.
444 ruby_port->physMemPort->sendFunctional(pkt);
445 }
446
447 // turn packet around to go back to requester if response expected
448 if (needsResponse) {
449 pkt->setFunctionalResponseStatus(accessSucceeded);
450 DPRINTF(RubyPort, "Sending packet back over port\n");
451 sendFunctional(pkt);
452 }
453 DPRINTF(RubyPort, "Functional access %s!\n",
454 accessSucceeded ? "successful":"failed");
455 }
456
457 void
458 RubyPort::ruby_hit_callback(PacketPtr pkt)
459 {
460 // Retrieve the request port from the sender State
461 RubyPort::SenderState *senderState =
462 safe_cast<RubyPort::SenderState *>(pkt->senderState);
463 M5Port *port = senderState->port;
464 assert(port != NULL);
465
466 // pop the sender state from the packet
467 pkt->senderState = senderState->saved;
468 delete senderState;
469
470 port->hitCallback(pkt);
471
472 //
473 // If we had to stall the M5Ports, wake them up because the sequencer
474 // likely has free resources now.
475 //
476 if (waitingOnSequencer) {
477 //
478 // Record the current list of ports to retry on a temporary list before
479 // calling sendRetry on those ports. sendRetry will cause an
480 // immediate retry, which may result in the ports being put back on the
481 // list. Therefore we want to clear the retryList before calling
482 // sendRetry.
483 //
484 std::list<M5Port*> curRetryList(retryList);
485
486 retryList.clear();
487 waitingOnSequencer = false;
488
489 for (std::list<M5Port*>::iterator i = curRetryList.begin();
490 i != curRetryList.end(); ++i) {
491 DPRINTF(RubyPort,
492 "Sequencer may now be free. SendRetry to port %s\n",
493 (*i)->name());
494 (*i)->onRetryList(false);
495 (*i)->sendRetry();
496 }
497 }
498 }
499
500 void
501 RubyPort::M5Port::hitCallback(PacketPtr pkt)
502 {
503 bool needsResponse = pkt->needsResponse();
504
505 //
506 // Unless specified at configuraiton, all responses except failed SC
507 // and Flush operations access M5 physical memory.
508 //
509 bool accessPhysMem = access_phys_mem;
510
511 if (pkt->isLLSC()) {
512 if (pkt->isWrite()) {
513 if (pkt->req->getExtraData() != 0) {
514 //
515 // Successful SC packets convert to normal writes
516 //
517 pkt->convertScToWrite();
518 } else {
519 //
520 // Failed SC packets don't access physical memory and thus
521 // the RubyPort itself must convert it to a response.
522 //
523 accessPhysMem = false;
524 }
525 } else {
526 //
527 // All LL packets convert to normal loads so that M5 PhysMem does
528 // not lock the blocks.
529 //
530 pkt->convertLlToRead();
531 }
532 }
533
534 //
535 // Flush requests don't access physical memory
536 //
537 if (pkt->isFlush()) {
538 accessPhysMem = false;
539 }
540
541 DPRINTF(RubyPort, "Hit callback needs response %d\n", needsResponse);
542
543 if (accessPhysMem) {
544 ruby_port->physMemPort->sendAtomic(pkt);
545 } else if (needsResponse) {
546 pkt->makeResponse();
547 }
548
549 // turn packet around to go back to requester if response expected
550 if (needsResponse) {
551 DPRINTF(RubyPort, "Sending packet back over port\n");
552 sendTiming(pkt);
553 } else {
554 delete pkt;
555 }
556 DPRINTF(RubyPort, "Hit callback done!\n");
557 }
558
559 bool
560 RubyPort::M5Port::sendTiming(PacketPtr pkt)
561 {
562 //minimum latency, must be > 0
563 schedSendTiming(pkt, curTick() + (1 * g_eventQueue_ptr->getClock()));
564 return true;
565 }
566
567 bool
568 RubyPort::PioPort::sendTiming(PacketPtr pkt)
569 {
570 //minimum latency, must be > 0
571 schedSendTiming(pkt, curTick() + (1 * g_eventQueue_ptr->getClock()));
572 return true;
573 }
574
575 bool
576 RubyPort::M5Port::isPhysMemAddress(Addr addr)
577 {
578 AddrRangeList physMemAddrList;
579 bool snoop = false;
580 ruby_port->physMemPort->getPeerAddressRanges(physMemAddrList, snoop);
581 for (AddrRangeIter iter = physMemAddrList.begin();
582 iter != physMemAddrList.end();
583 iter++) {
584 if (addr >= iter->start && addr <= iter->end) {
585 DPRINTF(RubyPort, "Request found in %#llx - %#llx range\n",
586 iter->start, iter->end);
587 return true;
588 }
589 }
590 return false;
591 }
592
593 unsigned
594 RubyPort::M5Port::deviceBlockSize() const
595 {
596 return (unsigned) RubySystem::getBlockSizeBytes();
597 }