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