3 * Copyright (c) 2009 Advanced Micro Devices, Inc.
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.
30 #include "mem/physical.hh"
31 #include "mem/ruby/system/RubyPort.hh"
32 #include "mem/ruby/slicc_interface/AbstractController.hh"
33 #include "cpu/rubytest/RubyTester.hh"
35 uint16_t RubyPort::m_num_ports
= 0;
37 RubyPort::RequestMap
RubyPort::pending_cpu_requests
;
39 RubyPort::RubyPort(const Params
*p
)
42 m_version
= p
->version
;
43 assert(m_version
!= -1);
48 m_mandatory_q_ptr
= NULL
;
50 m_port_id
= m_num_ports
++;
52 m_hit_callback
= ruby_hit_callback
;
55 assert(m_num_ports
<= 2048); // see below for reason
60 assert(m_controller
!= NULL
);
61 m_mandatory_q_ptr
= m_controller
->getMandatoryQueue();
65 RubyPort::getPort(const std::string
&if_name
, int idx
)
67 if (if_name
== "port") {
68 return new M5Port(csprintf("%s-port%d", name(), idx
), this);
69 } else if (if_name
== "pio_port") {
71 // ensure there is only one pio port
73 assert(pio_port
== NULL
);
75 pio_port
= new PioPort(csprintf("%s-pio-port%d", name(), idx
),
79 } else if (if_name
== "physMemPort") {
81 // RubyPort should only have one port to physical memory
83 assert (physMemPort
== NULL
);
85 physMemPort
= new M5Port(csprintf("%s-physMemPort", name()),
89 } else if (if_name
== "functional") {
91 // Calls for the functional port only want to access functional memory.
92 // Therefore, directly pass these calls ports to physmem.
94 assert(physmem
!= NULL
);
95 return physmem
->getPort(if_name
, idx
);
100 RubyPort::PioPort::PioPort(const std::string
&_name
,
102 : SimpleTimingPort(_name
, _port
)
104 DPRINTF(Ruby
, "creating port to ruby sequencer to cpu %s\n", _name
);
108 RubyPort::M5Port::M5Port(const std::string
&_name
,
110 : SimpleTimingPort(_name
, _port
)
112 DPRINTF(Ruby
, "creating port from ruby sequcner to cpu %s\n", _name
);
117 RubyPort::PioPort::recvAtomic(PacketPtr pkt
)
119 panic("RubyPort::PioPort::recvAtomic() not implemented!\n");
125 RubyPort::M5Port::recvAtomic(PacketPtr pkt
)
127 panic("RubyPort::M5Port::recvAtomic() not implemented!\n");
133 RubyPort::PioPort::recvTiming(PacketPtr pkt
)
136 // In FS mode, ruby memory will receive pio responses from devices and
137 // it must forward these responses back to the particular CPU.
139 DPRINTF(MemoryAccess
,
140 "Pio response for address %#x\n",
143 assert(pkt
->isResponse());
146 // First we must retrieve the request port from the sender State
148 RubyPort::SenderState
*senderState
=
149 safe_cast
<RubyPort::SenderState
*>(pkt
->senderState
);
150 M5Port
*port
= senderState
->port
;
151 assert(port
!= NULL
);
153 // pop the sender state from the packet
154 pkt
->senderState
= senderState
->saved
;
157 port
->sendTiming(pkt
);
163 RubyPort::M5Port::recvTiming(PacketPtr pkt
)
165 DPRINTF(MemoryAccess
,
166 "Timing access caught for address %#x\n",
169 //dsm: based on SimpleTimingPort::recvTiming(pkt);
172 // After checking for pio responses, the remainder of packets
173 // received by ruby should only be M5 requests, which should never
174 // get nacked. There used to be code to hanldle nacks here, but
175 // I'm pretty sure it didn't work correctly with the drain code,
176 // so that would need to be fixed if we ever added it back.
178 assert(pkt
->isRequest());
180 if (pkt
->memInhibitAsserted()) {
181 warn("memInhibitAsserted???");
182 // snooper will supply based on copy of packet
183 // still target's responsibility to delete packet
189 // Check for pio requests and directly send them to the dedicated
192 if (!isPhysMemAddress(pkt
->getAddr())) {
193 assert(ruby_port
->pio_port
!= NULL
);
196 // Save the port in the sender state object to be used later to
197 // route the response
199 pkt
->senderState
= new SenderState(this, pkt
->senderState
);
201 return ruby_port
->pio_port
->sendTiming(pkt
);
205 // For DMA and CPU requests, translate them to ruby requests before
206 // sending them to our assigned ruby port.
208 RubyRequestType type
= RubyRequestType_NULL
;
211 // If valid, copy the pc to the ruby request
214 if (pkt
->req
->hasPC()) {
215 pc
= pkt
->req
->getPC();
219 if (pkt
->req
->isInstFetch()) {
220 type
= RubyRequestType_IFETCH
;
222 type
= RubyRequestType_LD
;
224 } else if (pkt
->isWrite()) {
225 type
= RubyRequestType_ST
;
226 } else if (pkt
->isReadWrite()) {
227 type
= RubyRequestType_RMW_Write
;
230 RubyRequest
ruby_request(pkt
->getAddr(), pkt
->getPtr
<uint8_t>(),
231 pkt
->getSize(), pc
, type
,
232 RubyAccessMode_Supervisor
);
234 // Submit the ruby request
235 int64_t req_id
= ruby_port
->makeRequest(ruby_request
);
240 // Save the request for the callback
241 RubyPort::pending_cpu_requests
[req_id
] = new RequestCookie(pkt
, this);
247 RubyPort::ruby_hit_callback(int64_t req_id
)
250 // Note: This single fuction can be called by cpu and dma ports,
251 // as well as the functional port.
253 RequestMap::iterator i
= pending_cpu_requests
.find(req_id
);
254 if (i
== pending_cpu_requests
.end())
255 panic("could not find pending request %d\n", req_id
);
257 RequestCookie
*cookie
= i
->second
;
258 pending_cpu_requests
.erase(i
);
260 Packet
*pkt
= cookie
->pkt
;
261 M5Port
*port
= cookie
->m5Port
;
264 port
->hitCallback(pkt
);
268 RubyPort::M5Port::hitCallback(PacketPtr pkt
)
271 bool needsResponse
= pkt
->needsResponse();
273 DPRINTF(MemoryAccess
, "Hit callback needs response %d\n",
276 ruby_port
->physMemPort
->sendAtomic(pkt
);
278 // turn packet around to go back to requester if response expected
280 // sendAtomic() should already have turned packet into
282 assert(pkt
->isResponse());
283 DPRINTF(MemoryAccess
, "Sending packet back over port\n");
288 DPRINTF(MemoryAccess
, "Hit callback done!\n");
292 RubyPort::M5Port::sendTiming(PacketPtr pkt
)
294 schedSendTiming(pkt
, curTick
+ 1); //minimum latency, must be > 0
299 RubyPort::PioPort::sendTiming(PacketPtr pkt
)
301 schedSendTiming(pkt
, curTick
+ 1); //minimum latency, must be > 0
306 RubyPort::M5Port::isPhysMemAddress(Addr addr
)
308 AddrRangeList physMemAddrList
;
310 ruby_port
->physMemPort
->getPeerAddressRanges(physMemAddrList
, snoop
);
311 for(AddrRangeIter iter
= physMemAddrList
.begin();
312 iter
!= physMemAddrList
.end();
314 if (addr
>= iter
->start
&& addr
<= iter
->end
) {
315 DPRINTF(MemoryAccess
, "Request found in %#llx - %#llx range\n",
316 iter
->start
, iter
->end
);