2 * Copyright (c) 2009 Advanced Micro Devices, Inc.
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.
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.
29 #include "config/the_isa.hh"
30 #if THE_ISA == X86_ISA
31 #include "arch/x86/insts/microldstop.hh"
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"
40 RubyPort::RubyPort(const Params
*p
)
43 m_version
= p
->version
;
44 assert(m_version
!= -1);
49 m_mandatory_q_ptr
= NULL
;
55 m_usingRubyTester
= p
->using_ruby_tester
;
56 access_phys_mem
= p
->access_phys_mem
;
58 ruby_system
= p
->ruby_system
;
64 assert(m_controller
!= NULL
);
65 m_mandatory_q_ptr
= m_controller
->getMandatoryQueue();
69 RubyPort::getPort(const std::string
&if_name
, int idx
)
71 if (if_name
== "port") {
72 return new M5Port(csprintf("%s-port%d", name(), idx
), this,
73 ruby_system
, access_phys_mem
);
76 if (if_name
== "pio_port") {
77 // ensure there is only one pio port
78 assert(pio_port
== NULL
);
80 pio_port
= new PioPort(csprintf("%s-pio-port%d", name(), idx
), this);
85 if (if_name
== "physMemPort") {
86 // RubyPort should only have one port to physical memory
87 assert (physMemPort
== NULL
);
89 physMemPort
= new M5Port(csprintf("%s-physMemPort", name()), this,
90 ruby_system
, access_phys_mem
);
95 if (if_name
== "functional") {
96 // Calls for the functional port only want to access
97 // functional memory. Therefore, directly pass these calls
99 assert(physmem
!= NULL
);
100 return physmem
->getPort(if_name
, idx
);
106 RubyPort::PioPort::PioPort(const std::string
&_name
,
108 : SimpleTimingPort(_name
, _port
)
110 DPRINTF(RubyPort
, "creating port to ruby sequencer to cpu %s\n", _name
);
114 RubyPort::M5Port::M5Port(const std::string
&_name
, RubyPort
*_port
,
115 RubySystem
*_system
, bool _access_phys_mem
)
116 : SimpleTimingPort(_name
, _port
)
118 DPRINTF(RubyPort
, "creating port from ruby sequcner to cpu %s\n", _name
);
120 ruby_system
= _system
;
121 _onRetryList
= false;
122 access_phys_mem
= _access_phys_mem
;
126 RubyPort::PioPort::recvAtomic(PacketPtr pkt
)
128 panic("RubyPort::PioPort::recvAtomic() not implemented!\n");
133 RubyPort::M5Port::recvAtomic(PacketPtr pkt
)
135 panic("RubyPort::M5Port::recvAtomic() not implemented!\n");
141 RubyPort::PioPort::recvTiming(PacketPtr pkt
)
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());
147 assert(pkt
->isResponse());
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
);
155 // pop the sender state from the packet
156 pkt
->senderState
= senderState
->saved
;
159 port
->sendTiming(pkt
);
165 RubyPort::M5Port::recvTiming(PacketPtr pkt
)
168 "Timing access caught for address %#x\n", pkt
->getAddr());
170 //dsm: based on SimpleTimingPort::recvTiming(pkt);
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());
178 if (pkt
->memInhibitAsserted()) {
179 warn("memInhibitAsserted???");
180 // snooper will supply based on copy of packet
181 // still target's responsibility to delete packet
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
);
190 // Check for pio requests and directly send them to the dedicated
192 if (!isPhysMemAddress(pkt
->getAddr())) {
193 assert(ruby_port
->pio_port
!= NULL
);
195 "Request for address 0x%#x is assumed to be a pio request\n",
198 return ruby_port
->pio_port
->sendTiming(pkt
);
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
;
205 // If valid, copy the pc to the ruby request
207 if (pkt
->req
->hasPC()) {
208 pc
= pkt
->req
->getPC();
212 if (pkt
->isWrite()) {
213 DPRINTF(RubyPort
, "Issuing SC\n");
214 type
= RubyRequestType_Store_Conditional
;
216 DPRINTF(RubyPort
, "Issuing LL\n");
217 assert(pkt
->isRead());
218 type
= RubyRequestType_Load_Linked
;
220 } else if (pkt
->req
->isLocked()) {
221 if (pkt
->isWrite()) {
222 DPRINTF(RubyPort
, "Issuing Locked RMW Write\n");
223 type
= RubyRequestType_Locked_RMW_Write
;
225 DPRINTF(RubyPort
, "Issuing Locked RMW Read\n");
226 assert(pkt
->isRead());
227 type
= RubyRequestType_Locked_RMW_Read
;
231 if (pkt
->req
->isInstFetch()) {
232 type
= RubyRequestType_IFETCH
;
234 #if THE_ISA == X86_ISA
235 uint32_t flags
= pkt
->req
->getFlags();
236 bool storeCheck
= flags
&
237 (TheISA::StoreCheck
<< TheISA::FlagShift
);
239 bool storeCheck
= false;
242 type
= RubyRequestType_RMW_Read
;
244 type
= RubyRequestType_LD
;
247 } else if (pkt
->isWrite()) {
249 // Note: M5 packets do not differentiate ST from RMW_Write
251 type
= RubyRequestType_ST
;
252 } else if (pkt
->isFlush()) {
253 type
= RubyRequestType_FLUSH
;
255 panic("Unsupported ruby packet type\n");
259 RubyRequest
ruby_request(pkt
->getAddr(), pkt
->getPtr
<uint8_t>(true),
260 pkt
->getSize(), pc
, type
,
261 RubyAccessMode_Supervisor
, pkt
);
263 assert(ruby_request
.m_PhysicalAddress
.getOffset() + ruby_request
.m_Size
<=
264 RubySystem::getBlockSizeBytes());
266 // Submit the ruby request
267 RequestStatus requestStatus
= ruby_port
->makeRequest(ruby_request
);
269 // If the request successfully issued then we should return true.
270 // Otherwise, we need to delete the senderStatus we just created and return
272 if (requestStatus
== RequestStatus_Issued
) {
273 DPRINTF(RubyPort
, "Request %#x issued\n", pkt
->getAddr());
278 // Unless one is using the ruby tester, record the stalled M5 port for
279 // later retry when the sequencer becomes free.
281 if (!ruby_port
->m_usingRubyTester
) {
282 ruby_port
->addToRetryList(this);
286 "Request for address %#x did not issue because %s\n",
287 pkt
->getAddr(), RequestStatus_to_string(requestStatus
));
289 SenderState
* senderState
= safe_cast
<SenderState
*>(pkt
->senderState
);
290 pkt
->senderState
= senderState
->saved
;
296 RubyPort::M5Port::doFunctionalRead(PacketPtr pkt
)
298 Address
address(pkt
->getAddr());
299 Address
line_address(address
);
300 line_address
.makeLineAddress();
302 AccessPermission accessPerm
= AccessPermission_NotPresent
;
303 int num_controllers
= ruby_system
->m_abs_cntrl_vec
.size();
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.
309 DPRINTF(RubyPort
, "Functional Read request for %s\n",address
);
310 for(int i
= 0;i
< num_controllers
;++i
)
312 accessPerm
= ruby_system
->m_abs_cntrl_vec
[i
]
313 ->getAccessPermission(line_address
);
314 if(accessPerm
== AccessPermission_Read_Only
||
315 accessPerm
== AccessPermission_Read_Write
)
317 unsigned startByte
= address
.getAddress() - line_address
.getAddress();
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
);
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
)
328 data
[i
] = block
.getByte(i
+ startByte
);
337 RubyPort::M5Port::doFunctionalWrite(PacketPtr pkt
)
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();
344 DPRINTF(RubyPort
, "Functional Write request for %s\n",addr
);
346 unsigned int num_ro
= 0;
347 unsigned int num_rw
= 0;
348 unsigned int num_busy
= 0;
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
)
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
++;
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.
367 DPRINTF(RubyPort
, "num_busy = %d, num_ro = %d, num_rw = %d\n",
368 num_busy
, num_ro
, num_rw
);
370 if((num_busy
== 0 && num_ro
> 0) || num_rw
== 1)
372 uint8
* data
= pkt
->getPtr
<uint8_t>(true);
373 unsigned int size_in_bytes
= pkt
->getSize();
374 unsigned startByte
= addr
.getAddress() - line_addr
.getAddress();
376 for(int i
= 0; i
< num_controllers
;++i
)
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
)
384 DataBlock
& block
= ruby_system
->m_abs_cntrl_vec
[i
]
385 ->getDataBlock(line_addr
);
387 DPRINTF(RubyPort
, "%s\n",block
);
388 for (unsigned i
= 0; i
< size_in_bytes
; ++i
)
390 block
.setByte(i
+ startByte
, data
[i
]);
392 DPRINTF(RubyPort
, "%s\n",block
);
401 RubyPort::M5Port::recvFunctional(PacketPtr pkt
)
403 DPRINTF(RubyPort
, "Functional access caught for address %#x\n",
406 // Check for pio requests and directly send them to the dedicated
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",
412 panic("RubyPort::PioPort::recvFunctional() not implemented!\n");
415 assert(pkt
->getAddr() + pkt
->getSize() <=
416 line_address(Address(pkt
->getAddr())).getAddress() +
417 RubySystem::getBlockSizeBytes());
419 bool accessSucceeded
= false;
420 bool needsResponse
= pkt
->needsResponse();
422 // Do the functional access on ruby memory
424 accessSucceeded
= doFunctionalRead(pkt
);
425 } else if (pkt
->isWrite()) {
426 accessSucceeded
= doFunctionalWrite(pkt
);
428 panic("RubyPort: unsupported functional command %s\n",
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());
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
444 ruby_port
->physMemPort
->sendFunctional(pkt
);
447 // turn packet around to go back to requester if response expected
449 pkt
->setFunctionalResponseStatus(accessSucceeded
);
450 DPRINTF(RubyPort
, "Sending packet back over port\n");
453 DPRINTF(RubyPort
, "Functional access %s!\n",
454 accessSucceeded
? "successful":"failed");
458 RubyPort::ruby_hit_callback(PacketPtr pkt
)
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
);
466 // pop the sender state from the packet
467 pkt
->senderState
= senderState
->saved
;
470 port
->hitCallback(pkt
);
473 // If we had to stall the M5Ports, wake them up because the sequencer
474 // likely has free resources now.
476 if (waitingOnSequencer
) {
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
484 std::list
<M5Port
*> curRetryList(retryList
);
487 waitingOnSequencer
= false;
489 for (std::list
<M5Port
*>::iterator i
= curRetryList
.begin();
490 i
!= curRetryList
.end(); ++i
) {
492 "Sequencer may now be free. SendRetry to port %s\n",
494 (*i
)->onRetryList(false);
501 RubyPort::M5Port::hitCallback(PacketPtr pkt
)
503 bool needsResponse
= pkt
->needsResponse();
506 // Unless specified at configuraiton, all responses except failed SC
507 // and Flush operations access M5 physical memory.
509 bool accessPhysMem
= access_phys_mem
;
512 if (pkt
->isWrite()) {
513 if (pkt
->req
->getExtraData() != 0) {
515 // Successful SC packets convert to normal writes
517 pkt
->convertScToWrite();
520 // Failed SC packets don't access physical memory and thus
521 // the RubyPort itself must convert it to a response.
523 accessPhysMem
= false;
527 // All LL packets convert to normal loads so that M5 PhysMem does
528 // not lock the blocks.
530 pkt
->convertLlToRead();
535 // Flush requests don't access physical memory
537 if (pkt
->isFlush()) {
538 accessPhysMem
= false;
541 DPRINTF(RubyPort
, "Hit callback needs response %d\n", needsResponse
);
544 ruby_port
->physMemPort
->sendAtomic(pkt
);
545 } else if (needsResponse
) {
549 // turn packet around to go back to requester if response expected
551 DPRINTF(RubyPort
, "Sending packet back over port\n");
556 DPRINTF(RubyPort
, "Hit callback done!\n");
560 RubyPort::M5Port::sendTiming(PacketPtr pkt
)
562 //minimum latency, must be > 0
563 schedSendTiming(pkt
, curTick() + (1 * g_eventQueue_ptr
->getClock()));
568 RubyPort::PioPort::sendTiming(PacketPtr pkt
)
570 //minimum latency, must be > 0
571 schedSendTiming(pkt
, curTick() + (1 * g_eventQueue_ptr
->getClock()));
576 RubyPort::M5Port::isPhysMemAddress(Addr addr
)
578 AddrRangeList physMemAddrList
;
580 ruby_port
->physMemPort
->getPeerAddressRanges(physMemAddrList
, snoop
);
581 for (AddrRangeIter iter
= physMemAddrList
.begin();
582 iter
!= physMemAddrList
.end();
584 if (addr
>= iter
->start
&& addr
<= iter
->end
) {
585 DPRINTF(RubyPort
, "Request found in %#llx - %#llx range\n",
586 iter
->start
, iter
->end
);
594 RubyPort::M5Port::deviceBlockSize() const
596 return (unsigned) RubySystem::getBlockSizeBytes();