e4742dbab4f099174ba2064458e07d2fffe3061c
[gem5.git] / src / mem / ruby / system / RubyPort.cc
1
2 /*
3 * Copyright (c) 2009 Advanced Micro Devices, Inc.
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 #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"
34
35 uint16_t RubyPort::m_num_ports = 0;
36
37 RubyPort::RequestMap RubyPort::pending_cpu_requests;
38
39 RubyPort::RubyPort(const Params *p)
40 : MemObject(p)
41 {
42 m_version = p->version;
43 assert(m_version != -1);
44
45 physmem = p->physmem;
46
47 m_controller = NULL;
48 m_mandatory_q_ptr = NULL;
49
50 m_port_id = m_num_ports++;
51 m_request_cnt = 0;
52 m_hit_callback = ruby_hit_callback;
53 pio_port = NULL;
54 physMemPort = NULL;
55 assert(m_num_ports <= 2048); // see below for reason
56 }
57
58 void RubyPort::init()
59 {
60 assert(m_controller != NULL);
61 m_mandatory_q_ptr = m_controller->getMandatoryQueue();
62 }
63
64 Port *
65 RubyPort::getPort(const std::string &if_name, int idx)
66 {
67 if (if_name == "port") {
68 return new M5Port(csprintf("%s-port%d", name(), idx), this);
69 } else if (if_name == "pio_port") {
70 //
71 // ensure there is only one pio port
72 //
73 assert(pio_port == NULL);
74
75 pio_port = new PioPort(csprintf("%s-pio-port%d", name(), idx),
76 this);
77
78 return pio_port;
79 } else if (if_name == "physMemPort") {
80 //
81 // RubyPort should only have one port to physical memory
82 //
83 assert (physMemPort == NULL);
84
85 physMemPort = new M5Port(csprintf("%s-physMemPort", name()),
86 this);
87
88 return physMemPort;
89 } else if (if_name == "functional") {
90 //
91 // Calls for the functional port only want to access functional memory.
92 // Therefore, directly pass these calls ports to physmem.
93 //
94 assert(physmem != NULL);
95 return physmem->getPort(if_name, idx);
96 }
97 return NULL;
98 }
99
100 RubyPort::PioPort::PioPort(const std::string &_name,
101 RubyPort *_port)
102 : SimpleTimingPort(_name, _port)
103 {
104 DPRINTF(Ruby, "creating port to ruby sequencer to cpu %s\n", _name);
105 ruby_port = _port;
106 }
107
108 RubyPort::M5Port::M5Port(const std::string &_name,
109 RubyPort *_port)
110 : SimpleTimingPort(_name, _port)
111 {
112 DPRINTF(Ruby, "creating port from ruby sequcner to cpu %s\n", _name);
113 ruby_port = _port;
114 }
115
116 Tick
117 RubyPort::PioPort::recvAtomic(PacketPtr pkt)
118 {
119 panic("RubyPort::PioPort::recvAtomic() not implemented!\n");
120 return 0;
121 }
122
123
124 Tick
125 RubyPort::M5Port::recvAtomic(PacketPtr pkt)
126 {
127 panic("RubyPort::M5Port::recvAtomic() not implemented!\n");
128 return 0;
129 }
130
131
132 bool
133 RubyPort::PioPort::recvTiming(PacketPtr pkt)
134 {
135 //
136 // In FS mode, ruby memory will receive pio responses from devices and
137 // it must forward these responses back to the particular CPU.
138 //
139 DPRINTF(MemoryAccess,
140 "Pio response for address %#x\n",
141 pkt->getAddr());
142
143 assert(pkt->isResponse());
144
145 //
146 // First we must retrieve the request port from the sender State
147 //
148 RubyPort::SenderState *senderState =
149 safe_cast<RubyPort::SenderState *>(pkt->senderState);
150 M5Port *port = senderState->port;
151 assert(port != NULL);
152
153 // pop the sender state from the packet
154 pkt->senderState = senderState->saved;
155 delete senderState;
156
157 port->sendTiming(pkt);
158
159 return true;
160 }
161
162 bool
163 RubyPort::M5Port::recvTiming(PacketPtr pkt)
164 {
165 DPRINTF(MemoryAccess,
166 "Timing access caught for address %#x\n",
167 pkt->getAddr());
168
169 //dsm: based on SimpleTimingPort::recvTiming(pkt);
170
171 //
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.
177 //
178 assert(pkt->isRequest());
179
180 if (pkt->memInhibitAsserted()) {
181 warn("memInhibitAsserted???");
182 // snooper will supply based on copy of packet
183 // still target's responsibility to delete packet
184 delete pkt;
185 return true;
186 }
187
188 //
189 // Check for pio requests and directly send them to the dedicated
190 // pio port.
191 //
192 if (!isPhysMemAddress(pkt->getAddr())) {
193 assert(ruby_port->pio_port != NULL);
194
195 //
196 // Save the port in the sender state object to be used later to
197 // route the response
198 //
199 pkt->senderState = new SenderState(this, pkt->senderState);
200
201 return ruby_port->pio_port->sendTiming(pkt);
202 }
203
204 //
205 // For DMA and CPU requests, translate them to ruby requests before
206 // sending them to our assigned ruby port.
207 //
208 RubyRequestType type = RubyRequestType_NULL;
209
210 //
211 // If valid, copy the pc to the ruby request
212 //
213 Addr pc = 0;
214 if (pkt->req->hasPC()) {
215 pc = pkt->req->getPC();
216 }
217
218 if (pkt->isRead()) {
219 if (pkt->req->isInstFetch()) {
220 type = RubyRequestType_IFETCH;
221 } else {
222 type = RubyRequestType_LD;
223 }
224 } else if (pkt->isWrite()) {
225 type = RubyRequestType_ST;
226 } else if (pkt->isReadWrite()) {
227 type = RubyRequestType_RMW_Write;
228 }
229
230 RubyRequest ruby_request(pkt->getAddr(), pkt->getPtr<uint8_t>(),
231 pkt->getSize(), pc, type,
232 RubyAccessMode_Supervisor);
233
234 // Submit the ruby request
235 int64_t req_id = ruby_port->makeRequest(ruby_request);
236 if (req_id == -1) {
237 return false;
238 }
239
240 // Save the request for the callback
241 RubyPort::pending_cpu_requests[req_id] = new RequestCookie(pkt, this);
242
243 return true;
244 }
245
246 void
247 RubyPort::ruby_hit_callback(int64_t req_id)
248 {
249 //
250 // Note: This single fuction can be called by cpu and dma ports,
251 // as well as the functional port.
252 //
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);
256
257 RequestCookie *cookie = i->second;
258 pending_cpu_requests.erase(i);
259
260 Packet *pkt = cookie->pkt;
261 M5Port *port = cookie->m5Port;
262 delete cookie;
263
264 port->hitCallback(pkt);
265 }
266
267 void
268 RubyPort::M5Port::hitCallback(PacketPtr pkt)
269 {
270
271 bool needsResponse = pkt->needsResponse();
272
273 DPRINTF(MemoryAccess, "Hit callback needs response %d\n",
274 needsResponse);
275
276 ruby_port->physMemPort->sendAtomic(pkt);
277
278 // turn packet around to go back to requester if response expected
279 if (needsResponse) {
280 // sendAtomic() should already have turned packet into
281 // atomic response
282 assert(pkt->isResponse());
283 DPRINTF(MemoryAccess, "Sending packet back over port\n");
284 sendTiming(pkt);
285 } else {
286 delete pkt;
287 }
288 DPRINTF(MemoryAccess, "Hit callback done!\n");
289 }
290
291 bool
292 RubyPort::M5Port::sendTiming(PacketPtr pkt)
293 {
294 schedSendTiming(pkt, curTick + 1); //minimum latency, must be > 0
295 return true;
296 }
297
298 bool
299 RubyPort::PioPort::sendTiming(PacketPtr pkt)
300 {
301 schedSendTiming(pkt, curTick + 1); //minimum latency, must be > 0
302 return true;
303 }
304
305 bool
306 RubyPort::M5Port::isPhysMemAddress(Addr addr)
307 {
308 AddrRangeList physMemAddrList;
309 bool snoop = false;
310 ruby_port->physMemPort->getPeerAddressRanges(physMemAddrList, snoop);
311 for(AddrRangeIter iter = physMemAddrList.begin();
312 iter != physMemAddrList.end();
313 iter++) {
314 if (addr >= iter->start && addr <= iter->end) {
315 DPRINTF(MemoryAccess, "Request found in %#llx - %#llx range\n",
316 iter->start, iter->end);
317 return true;
318 }
319 }
320 return false;
321 }