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