RubyPort.cc (7558:6c3f81b176da) RubyPort.cc (7632:acf43d6bbc18)
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
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 "cpu/rubytest/RubyTester.hh"
29#include "cpu/testers/rubytest/RubyTester.hh"
30#include "mem/physical.hh"
31#include "mem/ruby/slicc_interface/AbstractController.hh"
32#include "mem/ruby/system/RubyPort.hh"
33
34RubyPort::RubyPort(const Params *p)
35 : MemObject(p)
36{
37 m_version = p->version;
38 assert(m_version != -1);
39
40 physmem = p->physmem;
41
42 m_controller = NULL;
43 m_mandatory_q_ptr = NULL;
44
45 m_request_cnt = 0;
46 pio_port = NULL;
47 physMemPort = NULL;
48}
49
50void
51RubyPort::init()
52{
53 assert(m_controller != NULL);
54 m_mandatory_q_ptr = m_controller->getMandatoryQueue();
55}
56
57Port *
58RubyPort::getPort(const std::string &if_name, int idx)
59{
60 if (if_name == "port") {
61 return new M5Port(csprintf("%s-port%d", name(), idx), this);
62 }
63
64 if (if_name == "pio_port") {
65 // ensure there is only one pio port
66 assert(pio_port == NULL);
67
68 pio_port = new PioPort(csprintf("%s-pio-port%d", name(), idx), this);
69
70 return pio_port;
71 }
72
73 if (if_name == "physMemPort") {
74 // RubyPort should only have one port to physical memory
75 assert (physMemPort == NULL);
76
77 physMemPort = new M5Port(csprintf("%s-physMemPort", name()), this);
78
79 return physMemPort;
80 }
81
82 if (if_name == "functional") {
83 // Calls for the functional port only want to access
84 // functional memory. Therefore, directly pass these calls
85 // ports to physmem.
86 assert(physmem != NULL);
87 return physmem->getPort(if_name, idx);
88 }
89
90 return NULL;
91}
92
93RubyPort::PioPort::PioPort(const std::string &_name,
94 RubyPort *_port)
95 : SimpleTimingPort(_name, _port)
96{
97 DPRINTF(Ruby, "creating port to ruby sequencer to cpu %s\n", _name);
98 ruby_port = _port;
99}
100
101RubyPort::M5Port::M5Port(const std::string &_name,
102 RubyPort *_port)
103 : SimpleTimingPort(_name, _port)
104{
105 DPRINTF(Ruby, "creating port from ruby sequcner to cpu %s\n", _name);
106 ruby_port = _port;
107}
108
109Tick
110RubyPort::PioPort::recvAtomic(PacketPtr pkt)
111{
112 panic("RubyPort::PioPort::recvAtomic() not implemented!\n");
113 return 0;
114}
115
116Tick
117RubyPort::M5Port::recvAtomic(PacketPtr pkt)
118{
119 panic("RubyPort::M5Port::recvAtomic() not implemented!\n");
120 return 0;
121}
122
123
124bool
125RubyPort::PioPort::recvTiming(PacketPtr pkt)
126{
127 // In FS mode, ruby memory will receive pio responses from devices
128 // and it must forward these responses back to the particular CPU.
129 DPRINTF(MemoryAccess, "Pio response for address %#x\n", pkt->getAddr());
130
131 assert(pkt->isResponse());
132
133 // First we must retrieve the request port from the sender State
134 RubyPort::SenderState *senderState =
135 safe_cast<RubyPort::SenderState *>(pkt->senderState);
136 M5Port *port = senderState->port;
137 assert(port != NULL);
138
139 // pop the sender state from the packet
140 pkt->senderState = senderState->saved;
141 delete senderState;
142
143 port->sendTiming(pkt);
144
145 return true;
146}
147
148bool
149RubyPort::M5Port::recvTiming(PacketPtr pkt)
150{
151 DPRINTF(MemoryAccess,
152 "Timing access caught for address %#x\n", pkt->getAddr());
153
154 //dsm: based on SimpleTimingPort::recvTiming(pkt);
155
156 // The received packets should only be M5 requests, which should never
157 // get nacked. There used to be code to hanldle nacks here, but
158 // I'm pretty sure it didn't work correctly with the drain code,
159 // so that would need to be fixed if we ever added it back.
160 assert(pkt->isRequest());
161
162 if (pkt->memInhibitAsserted()) {
163 warn("memInhibitAsserted???");
164 // snooper will supply based on copy of packet
165 // still target's responsibility to delete packet
166 delete pkt;
167 return true;
168 }
169
170 // Save the port in the sender state object to be used later to
171 // route the response
172 pkt->senderState = new SenderState(this, pkt->senderState);
173
174 // Check for pio requests and directly send them to the dedicated
175 // pio port.
176 if (!isPhysMemAddress(pkt->getAddr())) {
177 assert(ruby_port->pio_port != NULL);
178 DPRINTF(MemoryAccess,
179 "Request for address 0x%#x is assumed to be a pio request\n",
180 pkt->getAddr());
181
182 return ruby_port->pio_port->sendTiming(pkt);
183 }
184
185 // For DMA and CPU requests, translate them to ruby requests before
186 // sending them to our assigned ruby port.
187 RubyRequestType type = RubyRequestType_NULL;
188
189 // If valid, copy the pc to the ruby request
190 Addr pc = 0;
191 if (pkt->req->hasPC()) {
192 pc = pkt->req->getPC();
193 }
194
195 if (pkt->isLLSC()) {
196 if (pkt->isWrite()) {
197 DPRINTF(MemoryAccess, "Issuing SC\n");
198 type = RubyRequestType_Locked_Write;
199 } else {
200 DPRINTF(MemoryAccess, "Issuing LL\n");
201 assert(pkt->isRead());
202 type = RubyRequestType_Locked_Read;
203 }
204 } else {
205 if (pkt->isRead()) {
206 if (pkt->req->isInstFetch()) {
207 type = RubyRequestType_IFETCH;
208 } else {
209 type = RubyRequestType_LD;
210 }
211 } else if (pkt->isWrite()) {
212 type = RubyRequestType_ST;
213 } else if (pkt->isReadWrite()) {
214 // Fix me. This conditional will never be executed
215 // because isReadWrite() is just an OR of isRead() and
216 // isWrite(). Furthermore, just because the packet is a
217 // read/write request does not necessary mean it is a
218 // read-modify-write atomic operation.
219 type = RubyRequestType_RMW_Write;
220 } else {
221 panic("Unsupported ruby packet type\n");
222 }
223 }
224
225 RubyRequest ruby_request(pkt->getAddr(), pkt->getPtr<uint8_t>(),
226 pkt->getSize(), pc, type,
227 RubyAccessMode_Supervisor, pkt);
228
229 // Submit the ruby request
230 RequestStatus requestStatus = ruby_port->makeRequest(ruby_request);
231
232 // If the request successfully issued then we should return true.
233 // Otherwise, we need to delete the senderStatus we just created and return
234 // false.
235 if (requestStatus == RequestStatus_Issued) {
236 return true;
237 }
238
239 DPRINTF(MemoryAccess,
240 "Request for address #x did not issue because %s\n",
241 pkt->getAddr(), RequestStatus_to_string(requestStatus));
242
243 SenderState* senderState = safe_cast<SenderState*>(pkt->senderState);
244 pkt->senderState = senderState->saved;
245 delete senderState;
246 return false;
247}
248
249void
250RubyPort::ruby_hit_callback(PacketPtr pkt)
251{
252 // Retrieve the request port from the sender State
253 RubyPort::SenderState *senderState =
254 safe_cast<RubyPort::SenderState *>(pkt->senderState);
255 M5Port *port = senderState->port;
256 assert(port != NULL);
257
258 // pop the sender state from the packet
259 pkt->senderState = senderState->saved;
260 delete senderState;
261
262 port->hitCallback(pkt);
263}
264
265void
266RubyPort::M5Port::hitCallback(PacketPtr pkt)
267{
268 bool needsResponse = pkt->needsResponse();
269
270 //
271 // All responses except failed SC operations access M5 physical memory
272 //
273 bool accessPhysMem = true;
274
275 if (pkt->isLLSC()) {
276 if (pkt->isWrite()) {
277 if (pkt->req->getExtraData() != 0) {
278 //
279 // Successful SC packets convert to normal writes
280 //
281 pkt->convertScToWrite();
282 } else {
283 //
284 // Failed SC packets don't access physical memory and thus
285 // the RubyPort itself must convert it to a response.
286 //
287 accessPhysMem = false;
288 pkt->makeAtomicResponse();
289 }
290 } else {
291 //
292 // All LL packets convert to normal loads so that M5 PhysMem does
293 // not lock the blocks.
294 //
295 pkt->convertLlToRead();
296 }
297 }
298 DPRINTF(MemoryAccess, "Hit callback needs response %d\n", needsResponse);
299
300 if (accessPhysMem) {
301 ruby_port->physMemPort->sendAtomic(pkt);
302 }
303
304 // turn packet around to go back to requester if response expected
305 if (needsResponse) {
306 // sendAtomic() should already have turned packet into
307 // atomic response
308 assert(pkt->isResponse());
309 DPRINTF(MemoryAccess, "Sending packet back over port\n");
310 sendTiming(pkt);
311 } else {
312 delete pkt;
313 }
314 DPRINTF(MemoryAccess, "Hit callback done!\n");
315}
316
317bool
318RubyPort::M5Port::sendTiming(PacketPtr pkt)
319{
320 //minimum latency, must be > 0
321 schedSendTiming(pkt, curTick + (1 * g_eventQueue_ptr->getClock()));
322 return true;
323}
324
325bool
326RubyPort::PioPort::sendTiming(PacketPtr pkt)
327{
328 //minimum latency, must be > 0
329 schedSendTiming(pkt, curTick + (1 * g_eventQueue_ptr->getClock()));
330 return true;
331}
332
333bool
334RubyPort::M5Port::isPhysMemAddress(Addr addr)
335{
336 AddrRangeList physMemAddrList;
337 bool snoop = false;
338 ruby_port->physMemPort->getPeerAddressRanges(physMemAddrList, snoop);
339 for (AddrRangeIter iter = physMemAddrList.begin();
340 iter != physMemAddrList.end();
341 iter++) {
342 if (addr >= iter->start && addr <= iter->end) {
343 DPRINTF(MemoryAccess, "Request found in %#llx - %#llx range\n",
344 iter->start, iter->end);
345 return true;
346 }
347 }
348 return false;
349}
30#include "mem/physical.hh"
31#include "mem/ruby/slicc_interface/AbstractController.hh"
32#include "mem/ruby/system/RubyPort.hh"
33
34RubyPort::RubyPort(const Params *p)
35 : MemObject(p)
36{
37 m_version = p->version;
38 assert(m_version != -1);
39
40 physmem = p->physmem;
41
42 m_controller = NULL;
43 m_mandatory_q_ptr = NULL;
44
45 m_request_cnt = 0;
46 pio_port = NULL;
47 physMemPort = NULL;
48}
49
50void
51RubyPort::init()
52{
53 assert(m_controller != NULL);
54 m_mandatory_q_ptr = m_controller->getMandatoryQueue();
55}
56
57Port *
58RubyPort::getPort(const std::string &if_name, int idx)
59{
60 if (if_name == "port") {
61 return new M5Port(csprintf("%s-port%d", name(), idx), this);
62 }
63
64 if (if_name == "pio_port") {
65 // ensure there is only one pio port
66 assert(pio_port == NULL);
67
68 pio_port = new PioPort(csprintf("%s-pio-port%d", name(), idx), this);
69
70 return pio_port;
71 }
72
73 if (if_name == "physMemPort") {
74 // RubyPort should only have one port to physical memory
75 assert (physMemPort == NULL);
76
77 physMemPort = new M5Port(csprintf("%s-physMemPort", name()), this);
78
79 return physMemPort;
80 }
81
82 if (if_name == "functional") {
83 // Calls for the functional port only want to access
84 // functional memory. Therefore, directly pass these calls
85 // ports to physmem.
86 assert(physmem != NULL);
87 return physmem->getPort(if_name, idx);
88 }
89
90 return NULL;
91}
92
93RubyPort::PioPort::PioPort(const std::string &_name,
94 RubyPort *_port)
95 : SimpleTimingPort(_name, _port)
96{
97 DPRINTF(Ruby, "creating port to ruby sequencer to cpu %s\n", _name);
98 ruby_port = _port;
99}
100
101RubyPort::M5Port::M5Port(const std::string &_name,
102 RubyPort *_port)
103 : SimpleTimingPort(_name, _port)
104{
105 DPRINTF(Ruby, "creating port from ruby sequcner to cpu %s\n", _name);
106 ruby_port = _port;
107}
108
109Tick
110RubyPort::PioPort::recvAtomic(PacketPtr pkt)
111{
112 panic("RubyPort::PioPort::recvAtomic() not implemented!\n");
113 return 0;
114}
115
116Tick
117RubyPort::M5Port::recvAtomic(PacketPtr pkt)
118{
119 panic("RubyPort::M5Port::recvAtomic() not implemented!\n");
120 return 0;
121}
122
123
124bool
125RubyPort::PioPort::recvTiming(PacketPtr pkt)
126{
127 // In FS mode, ruby memory will receive pio responses from devices
128 // and it must forward these responses back to the particular CPU.
129 DPRINTF(MemoryAccess, "Pio response for address %#x\n", pkt->getAddr());
130
131 assert(pkt->isResponse());
132
133 // First we must retrieve the request port from the sender State
134 RubyPort::SenderState *senderState =
135 safe_cast<RubyPort::SenderState *>(pkt->senderState);
136 M5Port *port = senderState->port;
137 assert(port != NULL);
138
139 // pop the sender state from the packet
140 pkt->senderState = senderState->saved;
141 delete senderState;
142
143 port->sendTiming(pkt);
144
145 return true;
146}
147
148bool
149RubyPort::M5Port::recvTiming(PacketPtr pkt)
150{
151 DPRINTF(MemoryAccess,
152 "Timing access caught for address %#x\n", pkt->getAddr());
153
154 //dsm: based on SimpleTimingPort::recvTiming(pkt);
155
156 // The received packets should only be M5 requests, which should never
157 // get nacked. There used to be code to hanldle nacks here, but
158 // I'm pretty sure it didn't work correctly with the drain code,
159 // so that would need to be fixed if we ever added it back.
160 assert(pkt->isRequest());
161
162 if (pkt->memInhibitAsserted()) {
163 warn("memInhibitAsserted???");
164 // snooper will supply based on copy of packet
165 // still target's responsibility to delete packet
166 delete pkt;
167 return true;
168 }
169
170 // Save the port in the sender state object to be used later to
171 // route the response
172 pkt->senderState = new SenderState(this, pkt->senderState);
173
174 // Check for pio requests and directly send them to the dedicated
175 // pio port.
176 if (!isPhysMemAddress(pkt->getAddr())) {
177 assert(ruby_port->pio_port != NULL);
178 DPRINTF(MemoryAccess,
179 "Request for address 0x%#x is assumed to be a pio request\n",
180 pkt->getAddr());
181
182 return ruby_port->pio_port->sendTiming(pkt);
183 }
184
185 // For DMA and CPU requests, translate them to ruby requests before
186 // sending them to our assigned ruby port.
187 RubyRequestType type = RubyRequestType_NULL;
188
189 // If valid, copy the pc to the ruby request
190 Addr pc = 0;
191 if (pkt->req->hasPC()) {
192 pc = pkt->req->getPC();
193 }
194
195 if (pkt->isLLSC()) {
196 if (pkt->isWrite()) {
197 DPRINTF(MemoryAccess, "Issuing SC\n");
198 type = RubyRequestType_Locked_Write;
199 } else {
200 DPRINTF(MemoryAccess, "Issuing LL\n");
201 assert(pkt->isRead());
202 type = RubyRequestType_Locked_Read;
203 }
204 } else {
205 if (pkt->isRead()) {
206 if (pkt->req->isInstFetch()) {
207 type = RubyRequestType_IFETCH;
208 } else {
209 type = RubyRequestType_LD;
210 }
211 } else if (pkt->isWrite()) {
212 type = RubyRequestType_ST;
213 } else if (pkt->isReadWrite()) {
214 // Fix me. This conditional will never be executed
215 // because isReadWrite() is just an OR of isRead() and
216 // isWrite(). Furthermore, just because the packet is a
217 // read/write request does not necessary mean it is a
218 // read-modify-write atomic operation.
219 type = RubyRequestType_RMW_Write;
220 } else {
221 panic("Unsupported ruby packet type\n");
222 }
223 }
224
225 RubyRequest ruby_request(pkt->getAddr(), pkt->getPtr<uint8_t>(),
226 pkt->getSize(), pc, type,
227 RubyAccessMode_Supervisor, pkt);
228
229 // Submit the ruby request
230 RequestStatus requestStatus = ruby_port->makeRequest(ruby_request);
231
232 // If the request successfully issued then we should return true.
233 // Otherwise, we need to delete the senderStatus we just created and return
234 // false.
235 if (requestStatus == RequestStatus_Issued) {
236 return true;
237 }
238
239 DPRINTF(MemoryAccess,
240 "Request for address #x did not issue because %s\n",
241 pkt->getAddr(), RequestStatus_to_string(requestStatus));
242
243 SenderState* senderState = safe_cast<SenderState*>(pkt->senderState);
244 pkt->senderState = senderState->saved;
245 delete senderState;
246 return false;
247}
248
249void
250RubyPort::ruby_hit_callback(PacketPtr pkt)
251{
252 // Retrieve the request port from the sender State
253 RubyPort::SenderState *senderState =
254 safe_cast<RubyPort::SenderState *>(pkt->senderState);
255 M5Port *port = senderState->port;
256 assert(port != NULL);
257
258 // pop the sender state from the packet
259 pkt->senderState = senderState->saved;
260 delete senderState;
261
262 port->hitCallback(pkt);
263}
264
265void
266RubyPort::M5Port::hitCallback(PacketPtr pkt)
267{
268 bool needsResponse = pkt->needsResponse();
269
270 //
271 // All responses except failed SC operations access M5 physical memory
272 //
273 bool accessPhysMem = true;
274
275 if (pkt->isLLSC()) {
276 if (pkt->isWrite()) {
277 if (pkt->req->getExtraData() != 0) {
278 //
279 // Successful SC packets convert to normal writes
280 //
281 pkt->convertScToWrite();
282 } else {
283 //
284 // Failed SC packets don't access physical memory and thus
285 // the RubyPort itself must convert it to a response.
286 //
287 accessPhysMem = false;
288 pkt->makeAtomicResponse();
289 }
290 } else {
291 //
292 // All LL packets convert to normal loads so that M5 PhysMem does
293 // not lock the blocks.
294 //
295 pkt->convertLlToRead();
296 }
297 }
298 DPRINTF(MemoryAccess, "Hit callback needs response %d\n", needsResponse);
299
300 if (accessPhysMem) {
301 ruby_port->physMemPort->sendAtomic(pkt);
302 }
303
304 // turn packet around to go back to requester if response expected
305 if (needsResponse) {
306 // sendAtomic() should already have turned packet into
307 // atomic response
308 assert(pkt->isResponse());
309 DPRINTF(MemoryAccess, "Sending packet back over port\n");
310 sendTiming(pkt);
311 } else {
312 delete pkt;
313 }
314 DPRINTF(MemoryAccess, "Hit callback done!\n");
315}
316
317bool
318RubyPort::M5Port::sendTiming(PacketPtr pkt)
319{
320 //minimum latency, must be > 0
321 schedSendTiming(pkt, curTick + (1 * g_eventQueue_ptr->getClock()));
322 return true;
323}
324
325bool
326RubyPort::PioPort::sendTiming(PacketPtr pkt)
327{
328 //minimum latency, must be > 0
329 schedSendTiming(pkt, curTick + (1 * g_eventQueue_ptr->getClock()));
330 return true;
331}
332
333bool
334RubyPort::M5Port::isPhysMemAddress(Addr addr)
335{
336 AddrRangeList physMemAddrList;
337 bool snoop = false;
338 ruby_port->physMemPort->getPeerAddressRanges(physMemAddrList, snoop);
339 for (AddrRangeIter iter = physMemAddrList.begin();
340 iter != physMemAddrList.end();
341 iter++) {
342 if (addr >= iter->start && addr <= iter->end) {
343 DPRINTF(MemoryAccess, "Request found in %#llx - %#llx range\n",
344 iter->start, iter->end);
345 return true;
346 }
347 }
348 return false;
349}