atomic.cc (7720:65d338a8dba4) atomic.cc (7823:dac01f14f20f)
1/*
2 * Copyright (c) 2002-2005 The Regents of The University of Michigan
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 * Authors: Steve Reinhardt
29 */
30
31#include "arch/locked_mem.hh"
32#include "arch/mmaped_ipr.hh"
33#include "arch/utility.hh"
34#include "base/bigint.hh"
35#include "config/the_isa.hh"
36#include "cpu/exetrace.hh"
37#include "cpu/simple/atomic.hh"
38#include "mem/packet.hh"
39#include "mem/packet_access.hh"
40#include "params/AtomicSimpleCPU.hh"
41#include "sim/faults.hh"
42#include "sim/system.hh"
43
44using namespace std;
45using namespace TheISA;
46
47AtomicSimpleCPU::TickEvent::TickEvent(AtomicSimpleCPU *c)
48 : Event(CPU_Tick_Pri), cpu(c)
49{
50}
51
52
53void
54AtomicSimpleCPU::TickEvent::process()
55{
56 cpu->tick();
57}
58
59const char *
60AtomicSimpleCPU::TickEvent::description() const
61{
62 return "AtomicSimpleCPU tick";
63}
64
65Port *
66AtomicSimpleCPU::getPort(const string &if_name, int idx)
67{
68 if (if_name == "dcache_port")
69 return &dcachePort;
70 else if (if_name == "icache_port")
71 return &icachePort;
72 else if (if_name == "physmem_port") {
73 hasPhysMemPort = true;
74 return &physmemPort;
75 }
76 else
77 panic("No Such Port\n");
78}
79
80void
81AtomicSimpleCPU::init()
82{
83 BaseCPU::init();
84#if FULL_SYSTEM
85 ThreadID size = threadContexts.size();
86 for (ThreadID i = 0; i < size; ++i) {
87 ThreadContext *tc = threadContexts[i];
88
89 // initialize CPU, including PC
90 TheISA::initCPU(tc, tc->contextId());
91 }
92#endif
93 if (hasPhysMemPort) {
94 bool snoop = false;
95 AddrRangeList pmAddrList;
96 physmemPort.getPeerAddressRanges(pmAddrList, snoop);
97 physMemAddr = *pmAddrList.begin();
98 }
99 // Atomic doesn't do MT right now, so contextId == threadId
100 ifetch_req.setThreadContext(_cpuId, 0); // Add thread ID if we add MT
101 data_read_req.setThreadContext(_cpuId, 0); // Add thread ID here too
102 data_write_req.setThreadContext(_cpuId, 0); // Add thread ID here too
103}
104
105bool
106AtomicSimpleCPU::CpuPort::recvTiming(PacketPtr pkt)
107{
108 panic("AtomicSimpleCPU doesn't expect recvTiming callback!");
109 return true;
110}
111
112Tick
113AtomicSimpleCPU::CpuPort::recvAtomic(PacketPtr pkt)
114{
115 //Snooping a coherence request, just return
116 return 0;
117}
118
119void
120AtomicSimpleCPU::CpuPort::recvFunctional(PacketPtr pkt)
121{
122 //No internal storage to update, just return
123 return;
124}
125
126void
127AtomicSimpleCPU::CpuPort::recvStatusChange(Status status)
128{
129 if (status == RangeChange) {
130 if (!snoopRangeSent) {
131 snoopRangeSent = true;
132 sendStatusChange(Port::RangeChange);
133 }
134 return;
135 }
136
137 panic("AtomicSimpleCPU doesn't expect recvStatusChange callback!");
138}
139
140void
141AtomicSimpleCPU::CpuPort::recvRetry()
142{
143 panic("AtomicSimpleCPU doesn't expect recvRetry callback!");
144}
145
146void
147AtomicSimpleCPU::DcachePort::setPeer(Port *port)
148{
149 Port::setPeer(port);
150
151#if FULL_SYSTEM
152 // Update the ThreadContext's memory ports (Functional/Virtual
153 // Ports)
154 cpu->tcBase()->connectMemPorts(cpu->tcBase());
155#endif
156}
157
158AtomicSimpleCPU::AtomicSimpleCPU(AtomicSimpleCPUParams *p)
159 : BaseSimpleCPU(p), tickEvent(this), width(p->width), locked(false),
160 simulate_data_stalls(p->simulate_data_stalls),
161 simulate_inst_stalls(p->simulate_inst_stalls),
162 icachePort(name() + "-iport", this), dcachePort(name() + "-iport", this),
163 physmemPort(name() + "-iport", this), hasPhysMemPort(false)
164{
165 _status = Idle;
166
167 icachePort.snoopRangeSent = false;
168 dcachePort.snoopRangeSent = false;
169
170}
171
172
173AtomicSimpleCPU::~AtomicSimpleCPU()
174{
175 if (tickEvent.scheduled()) {
176 deschedule(tickEvent);
177 }
178}
179
180void
181AtomicSimpleCPU::serialize(ostream &os)
182{
183 SimObject::State so_state = SimObject::getState();
184 SERIALIZE_ENUM(so_state);
185 SERIALIZE_SCALAR(locked);
186 BaseSimpleCPU::serialize(os);
187 nameOut(os, csprintf("%s.tickEvent", name()));
188 tickEvent.serialize(os);
189}
190
191void
192AtomicSimpleCPU::unserialize(Checkpoint *cp, const string &section)
193{
194 SimObject::State so_state;
195 UNSERIALIZE_ENUM(so_state);
196 UNSERIALIZE_SCALAR(locked);
197 BaseSimpleCPU::unserialize(cp, section);
198 tickEvent.unserialize(cp, csprintf("%s.tickEvent", section));
199}
200
201void
202AtomicSimpleCPU::resume()
203{
204 if (_status == Idle || _status == SwitchedOut)
205 return;
206
207 DPRINTF(SimpleCPU, "Resume\n");
208 assert(system->getMemoryMode() == Enums::atomic);
209
210 changeState(SimObject::Running);
211 if (thread->status() == ThreadContext::Active) {
212 if (!tickEvent.scheduled())
213 schedule(tickEvent, nextCycle());
214 }
215}
216
217void
218AtomicSimpleCPU::switchOut()
219{
220 assert(_status == Running || _status == Idle);
221 _status = SwitchedOut;
222
223 tickEvent.squash();
224}
225
226
227void
228AtomicSimpleCPU::takeOverFrom(BaseCPU *oldCPU)
229{
230 BaseCPU::takeOverFrom(oldCPU, &icachePort, &dcachePort);
231
232 assert(!tickEvent.scheduled());
233
234 // if any of this CPU's ThreadContexts are active, mark the CPU as
235 // running and schedule its tick event.
236 ThreadID size = threadContexts.size();
237 for (ThreadID i = 0; i < size; ++i) {
238 ThreadContext *tc = threadContexts[i];
239 if (tc->status() == ThreadContext::Active && _status != Running) {
240 _status = Running;
241 schedule(tickEvent, nextCycle());
242 break;
243 }
244 }
245 if (_status != Running) {
246 _status = Idle;
247 }
248 assert(threadContexts.size() == 1);
249 ifetch_req.setThreadContext(_cpuId, 0); // Add thread ID if we add MT
250 data_read_req.setThreadContext(_cpuId, 0); // Add thread ID here too
251 data_write_req.setThreadContext(_cpuId, 0); // Add thread ID here too
252}
253
254
255void
256AtomicSimpleCPU::activateContext(int thread_num, int delay)
257{
258 DPRINTF(SimpleCPU, "ActivateContext %d (%d cycles)\n", thread_num, delay);
259
260 assert(thread_num == 0);
261 assert(thread);
262
263 assert(_status == Idle);
264 assert(!tickEvent.scheduled());
265
266 notIdleFraction++;
267 numCycles += tickToCycles(thread->lastActivate - thread->lastSuspend);
268
269 //Make sure ticks are still on multiples of cycles
1/*
2 * Copyright (c) 2002-2005 The Regents of The University of Michigan
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 * Authors: Steve Reinhardt
29 */
30
31#include "arch/locked_mem.hh"
32#include "arch/mmaped_ipr.hh"
33#include "arch/utility.hh"
34#include "base/bigint.hh"
35#include "config/the_isa.hh"
36#include "cpu/exetrace.hh"
37#include "cpu/simple/atomic.hh"
38#include "mem/packet.hh"
39#include "mem/packet_access.hh"
40#include "params/AtomicSimpleCPU.hh"
41#include "sim/faults.hh"
42#include "sim/system.hh"
43
44using namespace std;
45using namespace TheISA;
46
47AtomicSimpleCPU::TickEvent::TickEvent(AtomicSimpleCPU *c)
48 : Event(CPU_Tick_Pri), cpu(c)
49{
50}
51
52
53void
54AtomicSimpleCPU::TickEvent::process()
55{
56 cpu->tick();
57}
58
59const char *
60AtomicSimpleCPU::TickEvent::description() const
61{
62 return "AtomicSimpleCPU tick";
63}
64
65Port *
66AtomicSimpleCPU::getPort(const string &if_name, int idx)
67{
68 if (if_name == "dcache_port")
69 return &dcachePort;
70 else if (if_name == "icache_port")
71 return &icachePort;
72 else if (if_name == "physmem_port") {
73 hasPhysMemPort = true;
74 return &physmemPort;
75 }
76 else
77 panic("No Such Port\n");
78}
79
80void
81AtomicSimpleCPU::init()
82{
83 BaseCPU::init();
84#if FULL_SYSTEM
85 ThreadID size = threadContexts.size();
86 for (ThreadID i = 0; i < size; ++i) {
87 ThreadContext *tc = threadContexts[i];
88
89 // initialize CPU, including PC
90 TheISA::initCPU(tc, tc->contextId());
91 }
92#endif
93 if (hasPhysMemPort) {
94 bool snoop = false;
95 AddrRangeList pmAddrList;
96 physmemPort.getPeerAddressRanges(pmAddrList, snoop);
97 physMemAddr = *pmAddrList.begin();
98 }
99 // Atomic doesn't do MT right now, so contextId == threadId
100 ifetch_req.setThreadContext(_cpuId, 0); // Add thread ID if we add MT
101 data_read_req.setThreadContext(_cpuId, 0); // Add thread ID here too
102 data_write_req.setThreadContext(_cpuId, 0); // Add thread ID here too
103}
104
105bool
106AtomicSimpleCPU::CpuPort::recvTiming(PacketPtr pkt)
107{
108 panic("AtomicSimpleCPU doesn't expect recvTiming callback!");
109 return true;
110}
111
112Tick
113AtomicSimpleCPU::CpuPort::recvAtomic(PacketPtr pkt)
114{
115 //Snooping a coherence request, just return
116 return 0;
117}
118
119void
120AtomicSimpleCPU::CpuPort::recvFunctional(PacketPtr pkt)
121{
122 //No internal storage to update, just return
123 return;
124}
125
126void
127AtomicSimpleCPU::CpuPort::recvStatusChange(Status status)
128{
129 if (status == RangeChange) {
130 if (!snoopRangeSent) {
131 snoopRangeSent = true;
132 sendStatusChange(Port::RangeChange);
133 }
134 return;
135 }
136
137 panic("AtomicSimpleCPU doesn't expect recvStatusChange callback!");
138}
139
140void
141AtomicSimpleCPU::CpuPort::recvRetry()
142{
143 panic("AtomicSimpleCPU doesn't expect recvRetry callback!");
144}
145
146void
147AtomicSimpleCPU::DcachePort::setPeer(Port *port)
148{
149 Port::setPeer(port);
150
151#if FULL_SYSTEM
152 // Update the ThreadContext's memory ports (Functional/Virtual
153 // Ports)
154 cpu->tcBase()->connectMemPorts(cpu->tcBase());
155#endif
156}
157
158AtomicSimpleCPU::AtomicSimpleCPU(AtomicSimpleCPUParams *p)
159 : BaseSimpleCPU(p), tickEvent(this), width(p->width), locked(false),
160 simulate_data_stalls(p->simulate_data_stalls),
161 simulate_inst_stalls(p->simulate_inst_stalls),
162 icachePort(name() + "-iport", this), dcachePort(name() + "-iport", this),
163 physmemPort(name() + "-iport", this), hasPhysMemPort(false)
164{
165 _status = Idle;
166
167 icachePort.snoopRangeSent = false;
168 dcachePort.snoopRangeSent = false;
169
170}
171
172
173AtomicSimpleCPU::~AtomicSimpleCPU()
174{
175 if (tickEvent.scheduled()) {
176 deschedule(tickEvent);
177 }
178}
179
180void
181AtomicSimpleCPU::serialize(ostream &os)
182{
183 SimObject::State so_state = SimObject::getState();
184 SERIALIZE_ENUM(so_state);
185 SERIALIZE_SCALAR(locked);
186 BaseSimpleCPU::serialize(os);
187 nameOut(os, csprintf("%s.tickEvent", name()));
188 tickEvent.serialize(os);
189}
190
191void
192AtomicSimpleCPU::unserialize(Checkpoint *cp, const string &section)
193{
194 SimObject::State so_state;
195 UNSERIALIZE_ENUM(so_state);
196 UNSERIALIZE_SCALAR(locked);
197 BaseSimpleCPU::unserialize(cp, section);
198 tickEvent.unserialize(cp, csprintf("%s.tickEvent", section));
199}
200
201void
202AtomicSimpleCPU::resume()
203{
204 if (_status == Idle || _status == SwitchedOut)
205 return;
206
207 DPRINTF(SimpleCPU, "Resume\n");
208 assert(system->getMemoryMode() == Enums::atomic);
209
210 changeState(SimObject::Running);
211 if (thread->status() == ThreadContext::Active) {
212 if (!tickEvent.scheduled())
213 schedule(tickEvent, nextCycle());
214 }
215}
216
217void
218AtomicSimpleCPU::switchOut()
219{
220 assert(_status == Running || _status == Idle);
221 _status = SwitchedOut;
222
223 tickEvent.squash();
224}
225
226
227void
228AtomicSimpleCPU::takeOverFrom(BaseCPU *oldCPU)
229{
230 BaseCPU::takeOverFrom(oldCPU, &icachePort, &dcachePort);
231
232 assert(!tickEvent.scheduled());
233
234 // if any of this CPU's ThreadContexts are active, mark the CPU as
235 // running and schedule its tick event.
236 ThreadID size = threadContexts.size();
237 for (ThreadID i = 0; i < size; ++i) {
238 ThreadContext *tc = threadContexts[i];
239 if (tc->status() == ThreadContext::Active && _status != Running) {
240 _status = Running;
241 schedule(tickEvent, nextCycle());
242 break;
243 }
244 }
245 if (_status != Running) {
246 _status = Idle;
247 }
248 assert(threadContexts.size() == 1);
249 ifetch_req.setThreadContext(_cpuId, 0); // Add thread ID if we add MT
250 data_read_req.setThreadContext(_cpuId, 0); // Add thread ID here too
251 data_write_req.setThreadContext(_cpuId, 0); // Add thread ID here too
252}
253
254
255void
256AtomicSimpleCPU::activateContext(int thread_num, int delay)
257{
258 DPRINTF(SimpleCPU, "ActivateContext %d (%d cycles)\n", thread_num, delay);
259
260 assert(thread_num == 0);
261 assert(thread);
262
263 assert(_status == Idle);
264 assert(!tickEvent.scheduled());
265
266 notIdleFraction++;
267 numCycles += tickToCycles(thread->lastActivate - thread->lastSuspend);
268
269 //Make sure ticks are still on multiples of cycles
270 schedule(tickEvent, nextCycle(curTick + ticks(delay)));
270 schedule(tickEvent, nextCycle(curTick() + ticks(delay)));
271 _status = Running;
272}
273
274
275void
276AtomicSimpleCPU::suspendContext(int thread_num)
277{
278 DPRINTF(SimpleCPU, "SuspendContext %d\n", thread_num);
279
280 assert(thread_num == 0);
281 assert(thread);
282
283 if (_status == Idle)
284 return;
285
286 assert(_status == Running);
287
288 // tick event may not be scheduled if this gets called from inside
289 // an instruction's execution, e.g. "quiesce"
290 if (tickEvent.scheduled())
291 deschedule(tickEvent);
292
293 notIdleFraction--;
294 _status = Idle;
295}
296
297
298Fault
299AtomicSimpleCPU::readBytes(Addr addr, uint8_t * data,
300 unsigned size, unsigned flags)
301{
302 // use the CPU's statically allocated read request and packet objects
303 Request *req = &data_read_req;
304
305 if (traceData) {
306 traceData->setAddr(addr);
307 }
308
309 //The block size of our peer.
310 unsigned blockSize = dcachePort.peerBlockSize();
311 //The size of the data we're trying to read.
312 int fullSize = size;
313
314 //The address of the second part of this access if it needs to be split
315 //across a cache line boundary.
316 Addr secondAddr = roundDown(addr + size - 1, blockSize);
317
318 if (secondAddr > addr)
319 size = secondAddr - addr;
320
321 dcache_latency = 0;
322
323 while (1) {
324 req->setVirt(0, addr, size, flags, thread->pcState().instAddr());
325
326 // translate to physical address
327 Fault fault = thread->dtb->translateAtomic(req, tc, BaseTLB::Read);
328
329 // Now do the access.
330 if (fault == NoFault && !req->getFlags().isSet(Request::NO_ACCESS)) {
331 Packet pkt = Packet(req,
332 req->isLLSC() ? MemCmd::LoadLockedReq : MemCmd::ReadReq,
333 Packet::Broadcast);
334 pkt.dataStatic(data);
335
336 if (req->isMmapedIpr())
337 dcache_latency += TheISA::handleIprRead(thread->getTC(), &pkt);
338 else {
339 if (hasPhysMemPort && pkt.getAddr() == physMemAddr)
340 dcache_latency += physmemPort.sendAtomic(&pkt);
341 else
342 dcache_latency += dcachePort.sendAtomic(&pkt);
343 }
344 dcache_access = true;
345
346 assert(!pkt.isError());
347
348 if (req->isLLSC()) {
349 TheISA::handleLockedRead(thread, req);
350 }
351 }
352
353 //If there's a fault, return it
354 if (fault != NoFault) {
355 if (req->isPrefetch()) {
356 return NoFault;
357 } else {
358 return fault;
359 }
360 }
361
362 //If we don't need to access a second cache line, stop now.
363 if (secondAddr <= addr)
364 {
365 if (req->isLocked() && fault == NoFault) {
366 assert(!locked);
367 locked = true;
368 }
369 return fault;
370 }
371
372 /*
373 * Set up for accessing the second cache line.
374 */
375
376 //Move the pointer we're reading into to the correct location.
377 data += size;
378 //Adjust the size to get the remaining bytes.
379 size = addr + fullSize - secondAddr;
380 //And access the right address.
381 addr = secondAddr;
382 }
383}
384
385
386template <class T>
387Fault
388AtomicSimpleCPU::read(Addr addr, T &data, unsigned flags)
389{
390 uint8_t *dataPtr = (uint8_t *)&data;
391 memset(dataPtr, 0, sizeof(data));
392 Fault fault = readBytes(addr, dataPtr, sizeof(data), flags);
393 if (fault == NoFault) {
394 data = gtoh(data);
395 if (traceData)
396 traceData->setData(data);
397 }
398 return fault;
399}
400
401#ifndef DOXYGEN_SHOULD_SKIP_THIS
402
403template
404Fault
405AtomicSimpleCPU::read(Addr addr, Twin32_t &data, unsigned flags);
406
407template
408Fault
409AtomicSimpleCPU::read(Addr addr, Twin64_t &data, unsigned flags);
410
411template
412Fault
413AtomicSimpleCPU::read(Addr addr, uint64_t &data, unsigned flags);
414
415template
416Fault
417AtomicSimpleCPU::read(Addr addr, uint32_t &data, unsigned flags);
418
419template
420Fault
421AtomicSimpleCPU::read(Addr addr, uint16_t &data, unsigned flags);
422
423template
424Fault
425AtomicSimpleCPU::read(Addr addr, uint8_t &data, unsigned flags);
426
427#endif //DOXYGEN_SHOULD_SKIP_THIS
428
429template<>
430Fault
431AtomicSimpleCPU::read(Addr addr, double &data, unsigned flags)
432{
433 return read(addr, *(uint64_t*)&data, flags);
434}
435
436template<>
437Fault
438AtomicSimpleCPU::read(Addr addr, float &data, unsigned flags)
439{
440 return read(addr, *(uint32_t*)&data, flags);
441}
442
443
444template<>
445Fault
446AtomicSimpleCPU::read(Addr addr, int32_t &data, unsigned flags)
447{
448 return read(addr, (uint32_t&)data, flags);
449}
450
451
452Fault
453AtomicSimpleCPU::writeBytes(uint8_t *data, unsigned size,
454 Addr addr, unsigned flags, uint64_t *res)
455{
456 // use the CPU's statically allocated write request and packet objects
457 Request *req = &data_write_req;
458
459 if (traceData) {
460 traceData->setAddr(addr);
461 }
462
463 //The block size of our peer.
464 unsigned blockSize = dcachePort.peerBlockSize();
465 //The size of the data we're trying to read.
466 int fullSize = size;
467
468 //The address of the second part of this access if it needs to be split
469 //across a cache line boundary.
470 Addr secondAddr = roundDown(addr + size - 1, blockSize);
471
472 if(secondAddr > addr)
473 size = secondAddr - addr;
474
475 dcache_latency = 0;
476
477 while(1) {
478 req->setVirt(0, addr, size, flags, thread->pcState().instAddr());
479
480 // translate to physical address
481 Fault fault = thread->dtb->translateAtomic(req, tc, BaseTLB::Write);
482
483 // Now do the access.
484 if (fault == NoFault) {
485 MemCmd cmd = MemCmd::WriteReq; // default
486 bool do_access = true; // flag to suppress cache access
487
488 if (req->isLLSC()) {
489 cmd = MemCmd::StoreCondReq;
490 do_access = TheISA::handleLockedWrite(thread, req);
491 } else if (req->isSwap()) {
492 cmd = MemCmd::SwapReq;
493 if (req->isCondSwap()) {
494 assert(res);
495 req->setExtraData(*res);
496 }
497 }
498
499 if (do_access && !req->getFlags().isSet(Request::NO_ACCESS)) {
500 Packet pkt = Packet(req, cmd, Packet::Broadcast);
501 pkt.dataStatic(data);
502
503 if (req->isMmapedIpr()) {
504 dcache_latency +=
505 TheISA::handleIprWrite(thread->getTC(), &pkt);
506 } else {
507 if (hasPhysMemPort && pkt.getAddr() == physMemAddr)
508 dcache_latency += physmemPort.sendAtomic(&pkt);
509 else
510 dcache_latency += dcachePort.sendAtomic(&pkt);
511 }
512 dcache_access = true;
513 assert(!pkt.isError());
514
515 if (req->isSwap()) {
516 assert(res);
517 memcpy(res, pkt.getPtr<uint8_t>(), fullSize);
518 }
519 }
520
521 if (res && !req->isSwap()) {
522 *res = req->getExtraData();
523 }
524 }
525
526 //If there's a fault or we don't need to access a second cache line,
527 //stop now.
528 if (fault != NoFault || secondAddr <= addr)
529 {
530 if (req->isLocked() && fault == NoFault) {
531 assert(locked);
532 locked = false;
533 }
534 if (fault != NoFault && req->isPrefetch()) {
535 return NoFault;
536 } else {
537 return fault;
538 }
539 }
540
541 /*
542 * Set up for accessing the second cache line.
543 */
544
545 //Move the pointer we're reading into to the correct location.
546 data += size;
547 //Adjust the size to get the remaining bytes.
548 size = addr + fullSize - secondAddr;
549 //And access the right address.
550 addr = secondAddr;
551 }
552}
553
554
555template <class T>
556Fault
557AtomicSimpleCPU::write(T data, Addr addr, unsigned flags, uint64_t *res)
558{
559 uint8_t *dataPtr = (uint8_t *)&data;
560 if (traceData)
561 traceData->setData(data);
562 data = htog(data);
563
564 Fault fault = writeBytes(dataPtr, sizeof(data), addr, flags, res);
565 if (fault == NoFault && data_write_req.isSwap()) {
566 *res = gtoh((T)*res);
567 }
568 return fault;
569}
570
571
572#ifndef DOXYGEN_SHOULD_SKIP_THIS
573
574template
575Fault
576AtomicSimpleCPU::write(Twin32_t data, Addr addr,
577 unsigned flags, uint64_t *res);
578
579template
580Fault
581AtomicSimpleCPU::write(Twin64_t data, Addr addr,
582 unsigned flags, uint64_t *res);
583
584template
585Fault
586AtomicSimpleCPU::write(uint64_t data, Addr addr,
587 unsigned flags, uint64_t *res);
588
589template
590Fault
591AtomicSimpleCPU::write(uint32_t data, Addr addr,
592 unsigned flags, uint64_t *res);
593
594template
595Fault
596AtomicSimpleCPU::write(uint16_t data, Addr addr,
597 unsigned flags, uint64_t *res);
598
599template
600Fault
601AtomicSimpleCPU::write(uint8_t data, Addr addr,
602 unsigned flags, uint64_t *res);
603
604#endif //DOXYGEN_SHOULD_SKIP_THIS
605
606template<>
607Fault
608AtomicSimpleCPU::write(double data, Addr addr, unsigned flags, uint64_t *res)
609{
610 return write(*(uint64_t*)&data, addr, flags, res);
611}
612
613template<>
614Fault
615AtomicSimpleCPU::write(float data, Addr addr, unsigned flags, uint64_t *res)
616{
617 return write(*(uint32_t*)&data, addr, flags, res);
618}
619
620
621template<>
622Fault
623AtomicSimpleCPU::write(int32_t data, Addr addr, unsigned flags, uint64_t *res)
624{
625 return write((uint32_t)data, addr, flags, res);
626}
627
628
629void
630AtomicSimpleCPU::tick()
631{
632 DPRINTF(SimpleCPU, "Tick\n");
633
634 Tick latency = 0;
635
636 for (int i = 0; i < width || locked; ++i) {
637 numCycles++;
638
639 if (!curStaticInst || !curStaticInst->isDelayedCommit())
640 checkForInterrupts();
641
642 checkPcEventQueue();
643
644 Fault fault = NoFault;
645
646 TheISA::PCState pcState = thread->pcState();
647
648 bool needToFetch = !isRomMicroPC(pcState.microPC()) &&
649 !curMacroStaticInst;
650 if (needToFetch) {
651 setupFetchRequest(&ifetch_req);
652 fault = thread->itb->translateAtomic(&ifetch_req, tc,
653 BaseTLB::Execute);
654 }
655
656 if (fault == NoFault) {
657 Tick icache_latency = 0;
658 bool icache_access = false;
659 dcache_access = false; // assume no dcache access
660
661 if (needToFetch) {
662 // This is commented out because the predecoder would act like
663 // a tiny cache otherwise. It wouldn't be flushed when needed
664 // like the I cache. It should be flushed, and when that works
665 // this code should be uncommented.
666 //Fetch more instruction memory if necessary
667 //if(predecoder.needMoreBytes())
668 //{
669 icache_access = true;
670 Packet ifetch_pkt = Packet(&ifetch_req, MemCmd::ReadReq,
671 Packet::Broadcast);
672 ifetch_pkt.dataStatic(&inst);
673
674 if (hasPhysMemPort && ifetch_pkt.getAddr() == physMemAddr)
675 icache_latency = physmemPort.sendAtomic(&ifetch_pkt);
676 else
677 icache_latency = icachePort.sendAtomic(&ifetch_pkt);
678
679 assert(!ifetch_pkt.isError());
680
681 // ifetch_req is initialized to read the instruction directly
682 // into the CPU object's inst field.
683 //}
684 }
685
686 preExecute();
687
688 if (curStaticInst) {
689 fault = curStaticInst->execute(this, traceData);
690
691 // keep an instruction count
692 if (fault == NoFault)
693 countInst();
694 else if (traceData && !DTRACE(ExecFaulting)) {
695 delete traceData;
696 traceData = NULL;
697 }
698
699 postExecute();
700 }
701
702 // @todo remove me after debugging with legion done
703 if (curStaticInst && (!curStaticInst->isMicroop() ||
704 curStaticInst->isFirstMicroop()))
705 instCnt++;
706
707 Tick stall_ticks = 0;
708 if (simulate_inst_stalls && icache_access)
709 stall_ticks += icache_latency;
710
711 if (simulate_data_stalls && dcache_access)
712 stall_ticks += dcache_latency;
713
714 if (stall_ticks) {
715 Tick stall_cycles = stall_ticks / ticks(1);
716 Tick aligned_stall_ticks = ticks(stall_cycles);
717
718 if (aligned_stall_ticks < stall_ticks)
719 aligned_stall_ticks += 1;
720
721 latency += aligned_stall_ticks;
722 }
723
724 }
725 if(fault != NoFault || !stayAtPC)
726 advancePC(fault);
727 }
728
729 // instruction takes at least one cycle
730 if (latency < ticks(1))
731 latency = ticks(1);
732
733 if (_status != Idle)
271 _status = Running;
272}
273
274
275void
276AtomicSimpleCPU::suspendContext(int thread_num)
277{
278 DPRINTF(SimpleCPU, "SuspendContext %d\n", thread_num);
279
280 assert(thread_num == 0);
281 assert(thread);
282
283 if (_status == Idle)
284 return;
285
286 assert(_status == Running);
287
288 // tick event may not be scheduled if this gets called from inside
289 // an instruction's execution, e.g. "quiesce"
290 if (tickEvent.scheduled())
291 deschedule(tickEvent);
292
293 notIdleFraction--;
294 _status = Idle;
295}
296
297
298Fault
299AtomicSimpleCPU::readBytes(Addr addr, uint8_t * data,
300 unsigned size, unsigned flags)
301{
302 // use the CPU's statically allocated read request and packet objects
303 Request *req = &data_read_req;
304
305 if (traceData) {
306 traceData->setAddr(addr);
307 }
308
309 //The block size of our peer.
310 unsigned blockSize = dcachePort.peerBlockSize();
311 //The size of the data we're trying to read.
312 int fullSize = size;
313
314 //The address of the second part of this access if it needs to be split
315 //across a cache line boundary.
316 Addr secondAddr = roundDown(addr + size - 1, blockSize);
317
318 if (secondAddr > addr)
319 size = secondAddr - addr;
320
321 dcache_latency = 0;
322
323 while (1) {
324 req->setVirt(0, addr, size, flags, thread->pcState().instAddr());
325
326 // translate to physical address
327 Fault fault = thread->dtb->translateAtomic(req, tc, BaseTLB::Read);
328
329 // Now do the access.
330 if (fault == NoFault && !req->getFlags().isSet(Request::NO_ACCESS)) {
331 Packet pkt = Packet(req,
332 req->isLLSC() ? MemCmd::LoadLockedReq : MemCmd::ReadReq,
333 Packet::Broadcast);
334 pkt.dataStatic(data);
335
336 if (req->isMmapedIpr())
337 dcache_latency += TheISA::handleIprRead(thread->getTC(), &pkt);
338 else {
339 if (hasPhysMemPort && pkt.getAddr() == physMemAddr)
340 dcache_latency += physmemPort.sendAtomic(&pkt);
341 else
342 dcache_latency += dcachePort.sendAtomic(&pkt);
343 }
344 dcache_access = true;
345
346 assert(!pkt.isError());
347
348 if (req->isLLSC()) {
349 TheISA::handleLockedRead(thread, req);
350 }
351 }
352
353 //If there's a fault, return it
354 if (fault != NoFault) {
355 if (req->isPrefetch()) {
356 return NoFault;
357 } else {
358 return fault;
359 }
360 }
361
362 //If we don't need to access a second cache line, stop now.
363 if (secondAddr <= addr)
364 {
365 if (req->isLocked() && fault == NoFault) {
366 assert(!locked);
367 locked = true;
368 }
369 return fault;
370 }
371
372 /*
373 * Set up for accessing the second cache line.
374 */
375
376 //Move the pointer we're reading into to the correct location.
377 data += size;
378 //Adjust the size to get the remaining bytes.
379 size = addr + fullSize - secondAddr;
380 //And access the right address.
381 addr = secondAddr;
382 }
383}
384
385
386template <class T>
387Fault
388AtomicSimpleCPU::read(Addr addr, T &data, unsigned flags)
389{
390 uint8_t *dataPtr = (uint8_t *)&data;
391 memset(dataPtr, 0, sizeof(data));
392 Fault fault = readBytes(addr, dataPtr, sizeof(data), flags);
393 if (fault == NoFault) {
394 data = gtoh(data);
395 if (traceData)
396 traceData->setData(data);
397 }
398 return fault;
399}
400
401#ifndef DOXYGEN_SHOULD_SKIP_THIS
402
403template
404Fault
405AtomicSimpleCPU::read(Addr addr, Twin32_t &data, unsigned flags);
406
407template
408Fault
409AtomicSimpleCPU::read(Addr addr, Twin64_t &data, unsigned flags);
410
411template
412Fault
413AtomicSimpleCPU::read(Addr addr, uint64_t &data, unsigned flags);
414
415template
416Fault
417AtomicSimpleCPU::read(Addr addr, uint32_t &data, unsigned flags);
418
419template
420Fault
421AtomicSimpleCPU::read(Addr addr, uint16_t &data, unsigned flags);
422
423template
424Fault
425AtomicSimpleCPU::read(Addr addr, uint8_t &data, unsigned flags);
426
427#endif //DOXYGEN_SHOULD_SKIP_THIS
428
429template<>
430Fault
431AtomicSimpleCPU::read(Addr addr, double &data, unsigned flags)
432{
433 return read(addr, *(uint64_t*)&data, flags);
434}
435
436template<>
437Fault
438AtomicSimpleCPU::read(Addr addr, float &data, unsigned flags)
439{
440 return read(addr, *(uint32_t*)&data, flags);
441}
442
443
444template<>
445Fault
446AtomicSimpleCPU::read(Addr addr, int32_t &data, unsigned flags)
447{
448 return read(addr, (uint32_t&)data, flags);
449}
450
451
452Fault
453AtomicSimpleCPU::writeBytes(uint8_t *data, unsigned size,
454 Addr addr, unsigned flags, uint64_t *res)
455{
456 // use the CPU's statically allocated write request and packet objects
457 Request *req = &data_write_req;
458
459 if (traceData) {
460 traceData->setAddr(addr);
461 }
462
463 //The block size of our peer.
464 unsigned blockSize = dcachePort.peerBlockSize();
465 //The size of the data we're trying to read.
466 int fullSize = size;
467
468 //The address of the second part of this access if it needs to be split
469 //across a cache line boundary.
470 Addr secondAddr = roundDown(addr + size - 1, blockSize);
471
472 if(secondAddr > addr)
473 size = secondAddr - addr;
474
475 dcache_latency = 0;
476
477 while(1) {
478 req->setVirt(0, addr, size, flags, thread->pcState().instAddr());
479
480 // translate to physical address
481 Fault fault = thread->dtb->translateAtomic(req, tc, BaseTLB::Write);
482
483 // Now do the access.
484 if (fault == NoFault) {
485 MemCmd cmd = MemCmd::WriteReq; // default
486 bool do_access = true; // flag to suppress cache access
487
488 if (req->isLLSC()) {
489 cmd = MemCmd::StoreCondReq;
490 do_access = TheISA::handleLockedWrite(thread, req);
491 } else if (req->isSwap()) {
492 cmd = MemCmd::SwapReq;
493 if (req->isCondSwap()) {
494 assert(res);
495 req->setExtraData(*res);
496 }
497 }
498
499 if (do_access && !req->getFlags().isSet(Request::NO_ACCESS)) {
500 Packet pkt = Packet(req, cmd, Packet::Broadcast);
501 pkt.dataStatic(data);
502
503 if (req->isMmapedIpr()) {
504 dcache_latency +=
505 TheISA::handleIprWrite(thread->getTC(), &pkt);
506 } else {
507 if (hasPhysMemPort && pkt.getAddr() == physMemAddr)
508 dcache_latency += physmemPort.sendAtomic(&pkt);
509 else
510 dcache_latency += dcachePort.sendAtomic(&pkt);
511 }
512 dcache_access = true;
513 assert(!pkt.isError());
514
515 if (req->isSwap()) {
516 assert(res);
517 memcpy(res, pkt.getPtr<uint8_t>(), fullSize);
518 }
519 }
520
521 if (res && !req->isSwap()) {
522 *res = req->getExtraData();
523 }
524 }
525
526 //If there's a fault or we don't need to access a second cache line,
527 //stop now.
528 if (fault != NoFault || secondAddr <= addr)
529 {
530 if (req->isLocked() && fault == NoFault) {
531 assert(locked);
532 locked = false;
533 }
534 if (fault != NoFault && req->isPrefetch()) {
535 return NoFault;
536 } else {
537 return fault;
538 }
539 }
540
541 /*
542 * Set up for accessing the second cache line.
543 */
544
545 //Move the pointer we're reading into to the correct location.
546 data += size;
547 //Adjust the size to get the remaining bytes.
548 size = addr + fullSize - secondAddr;
549 //And access the right address.
550 addr = secondAddr;
551 }
552}
553
554
555template <class T>
556Fault
557AtomicSimpleCPU::write(T data, Addr addr, unsigned flags, uint64_t *res)
558{
559 uint8_t *dataPtr = (uint8_t *)&data;
560 if (traceData)
561 traceData->setData(data);
562 data = htog(data);
563
564 Fault fault = writeBytes(dataPtr, sizeof(data), addr, flags, res);
565 if (fault == NoFault && data_write_req.isSwap()) {
566 *res = gtoh((T)*res);
567 }
568 return fault;
569}
570
571
572#ifndef DOXYGEN_SHOULD_SKIP_THIS
573
574template
575Fault
576AtomicSimpleCPU::write(Twin32_t data, Addr addr,
577 unsigned flags, uint64_t *res);
578
579template
580Fault
581AtomicSimpleCPU::write(Twin64_t data, Addr addr,
582 unsigned flags, uint64_t *res);
583
584template
585Fault
586AtomicSimpleCPU::write(uint64_t data, Addr addr,
587 unsigned flags, uint64_t *res);
588
589template
590Fault
591AtomicSimpleCPU::write(uint32_t data, Addr addr,
592 unsigned flags, uint64_t *res);
593
594template
595Fault
596AtomicSimpleCPU::write(uint16_t data, Addr addr,
597 unsigned flags, uint64_t *res);
598
599template
600Fault
601AtomicSimpleCPU::write(uint8_t data, Addr addr,
602 unsigned flags, uint64_t *res);
603
604#endif //DOXYGEN_SHOULD_SKIP_THIS
605
606template<>
607Fault
608AtomicSimpleCPU::write(double data, Addr addr, unsigned flags, uint64_t *res)
609{
610 return write(*(uint64_t*)&data, addr, flags, res);
611}
612
613template<>
614Fault
615AtomicSimpleCPU::write(float data, Addr addr, unsigned flags, uint64_t *res)
616{
617 return write(*(uint32_t*)&data, addr, flags, res);
618}
619
620
621template<>
622Fault
623AtomicSimpleCPU::write(int32_t data, Addr addr, unsigned flags, uint64_t *res)
624{
625 return write((uint32_t)data, addr, flags, res);
626}
627
628
629void
630AtomicSimpleCPU::tick()
631{
632 DPRINTF(SimpleCPU, "Tick\n");
633
634 Tick latency = 0;
635
636 for (int i = 0; i < width || locked; ++i) {
637 numCycles++;
638
639 if (!curStaticInst || !curStaticInst->isDelayedCommit())
640 checkForInterrupts();
641
642 checkPcEventQueue();
643
644 Fault fault = NoFault;
645
646 TheISA::PCState pcState = thread->pcState();
647
648 bool needToFetch = !isRomMicroPC(pcState.microPC()) &&
649 !curMacroStaticInst;
650 if (needToFetch) {
651 setupFetchRequest(&ifetch_req);
652 fault = thread->itb->translateAtomic(&ifetch_req, tc,
653 BaseTLB::Execute);
654 }
655
656 if (fault == NoFault) {
657 Tick icache_latency = 0;
658 bool icache_access = false;
659 dcache_access = false; // assume no dcache access
660
661 if (needToFetch) {
662 // This is commented out because the predecoder would act like
663 // a tiny cache otherwise. It wouldn't be flushed when needed
664 // like the I cache. It should be flushed, and when that works
665 // this code should be uncommented.
666 //Fetch more instruction memory if necessary
667 //if(predecoder.needMoreBytes())
668 //{
669 icache_access = true;
670 Packet ifetch_pkt = Packet(&ifetch_req, MemCmd::ReadReq,
671 Packet::Broadcast);
672 ifetch_pkt.dataStatic(&inst);
673
674 if (hasPhysMemPort && ifetch_pkt.getAddr() == physMemAddr)
675 icache_latency = physmemPort.sendAtomic(&ifetch_pkt);
676 else
677 icache_latency = icachePort.sendAtomic(&ifetch_pkt);
678
679 assert(!ifetch_pkt.isError());
680
681 // ifetch_req is initialized to read the instruction directly
682 // into the CPU object's inst field.
683 //}
684 }
685
686 preExecute();
687
688 if (curStaticInst) {
689 fault = curStaticInst->execute(this, traceData);
690
691 // keep an instruction count
692 if (fault == NoFault)
693 countInst();
694 else if (traceData && !DTRACE(ExecFaulting)) {
695 delete traceData;
696 traceData = NULL;
697 }
698
699 postExecute();
700 }
701
702 // @todo remove me after debugging with legion done
703 if (curStaticInst && (!curStaticInst->isMicroop() ||
704 curStaticInst->isFirstMicroop()))
705 instCnt++;
706
707 Tick stall_ticks = 0;
708 if (simulate_inst_stalls && icache_access)
709 stall_ticks += icache_latency;
710
711 if (simulate_data_stalls && dcache_access)
712 stall_ticks += dcache_latency;
713
714 if (stall_ticks) {
715 Tick stall_cycles = stall_ticks / ticks(1);
716 Tick aligned_stall_ticks = ticks(stall_cycles);
717
718 if (aligned_stall_ticks < stall_ticks)
719 aligned_stall_ticks += 1;
720
721 latency += aligned_stall_ticks;
722 }
723
724 }
725 if(fault != NoFault || !stayAtPC)
726 advancePC(fault);
727 }
728
729 // instruction takes at least one cycle
730 if (latency < ticks(1))
731 latency = ticks(1);
732
733 if (_status != Idle)
734 schedule(tickEvent, curTick + latency);
734 schedule(tickEvent, curTick() + latency);
735}
736
737
738void
739AtomicSimpleCPU::printAddr(Addr a)
740{
741 dcachePort.printAddr(a);
742}
743
744
745////////////////////////////////////////////////////////////////////////
746//
747// AtomicSimpleCPU Simulation Object
748//
749AtomicSimpleCPU *
750AtomicSimpleCPUParams::create()
751{
752 numThreads = 1;
753#if !FULL_SYSTEM
754 if (workload.size() != 1)
755 panic("only one workload allowed");
756#endif
757 return new AtomicSimpleCPU(this);
758}
735}
736
737
738void
739AtomicSimpleCPU::printAddr(Addr a)
740{
741 dcachePort.printAddr(a);
742}
743
744
745////////////////////////////////////////////////////////////////////////
746//
747// AtomicSimpleCPU Simulation Object
748//
749AtomicSimpleCPU *
750AtomicSimpleCPUParams::create()
751{
752 numThreads = 1;
753#if !FULL_SYSTEM
754 if (workload.size() != 1)
755 panic("only one workload allowed");
756#endif
757 return new AtomicSimpleCPU(this);
758}