summaryrefslogtreecommitdiff
path: root/cpu/beta_cpu/iew_impl.hh
blob: 521ce77f64651203f53de61e5aa9cf86e7ce1fcd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
// @todo: Fix the instantaneous communication among all the stages within
// iew.  There's a clear delay between issue and execute, yet backwards
// communication happens simultaneously.  Might not be that bad really...
// it might skew stats a bit though.  Issue would otherwise try to issue
// instructions that would never be executed if there were a delay; without
// it issue will simply squash.  Make this stage block properly.
// Update the statuses for each stage.
// Actually read instructions out of the skid buffer.

#include <queue>

#include "base/timebuf.hh"
#include "cpu/beta_cpu/iew.hh"

template<class Impl, class IQ>
SimpleIEW<Impl, IQ>::SimpleIEW(Params &params)
    : // Just make this time buffer really big for now
      issueToExecQueue(5, 5),
      instQueue(params),
      ldstQueue(params),
      commitToIEWDelay(params.commitToIEWDelay),
      renameToIEWDelay(params.renameToIEWDelay),
      issueToExecuteDelay(params.issueToExecuteDelay),
      issueReadWidth(params.issueWidth),
      issueWidth(params.issueWidth),
      executeWidth(params.executeWidth)
{
    DPRINTF(IEW, "IEW: executeIntWidth: %i.\n", params.executeIntWidth);
    _status = Idle;
    _issueStatus = Idle;
    _exeStatus = Idle;
    _wbStatus = Idle;

    // Setup wire to read instructions coming from issue.
    fromIssue = issueToExecQueue.getWire(-issueToExecuteDelay);

    // Instruction queue needs the queue between issue and execute.
    instQueue.setIssueToExecuteQueue(&issueToExecQueue);
}

template<class Impl, class IQ>
void
SimpleIEW<Impl, IQ>::setCPU(FullCPU *cpu_ptr)
{
    DPRINTF(IEW, "IEW: Setting CPU pointer.\n");
    cpu = cpu_ptr;

    instQueue.setCPU(cpu_ptr);
    ldstQueue.setCPU(cpu_ptr);
}

template<class Impl, class IQ>
void
SimpleIEW<Impl, IQ>::setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr)
{
    DPRINTF(IEW, "IEW: Setting time buffer pointer.\n");
    timeBuffer = tb_ptr;

    // Setup wire to read information from time buffer, from commit.
    fromCommit = timeBuffer->getWire(-commitToIEWDelay);

    // Setup wire to write information back to previous stages.
    toRename = timeBuffer->getWire(0);

    // Instruction queue also needs main time buffer.
    instQueue.setTimeBuffer(tb_ptr);
}

template<class Impl, class IQ>
void
SimpleIEW<Impl, IQ>::setRenameQueue(TimeBuffer<RenameStruct> *rq_ptr)
{
    DPRINTF(IEW, "IEW: Setting rename queue pointer.\n");
    renameQueue = rq_ptr;

    // Setup wire to read information from rename queue.
    fromRename = renameQueue->getWire(-renameToIEWDelay);
}

template<class Impl, class IQ>
void
SimpleIEW<Impl, IQ>::setIEWQueue(TimeBuffer<IEWStruct> *iq_ptr)
{
    DPRINTF(IEW, "IEW: Setting IEW queue pointer.\n");
    iewQueue = iq_ptr;

    // Setup wire to write instructions to commit.
    toCommit = iewQueue->getWire(0);
}

template<class Impl, class IQ>
void
SimpleIEW<Impl, IQ>::setRenameMap(RenameMap *rm_ptr)
{
    DPRINTF(IEW, "IEW: Setting rename map pointer.\n");
    renameMap = rm_ptr;
}

template<class Impl, class IQ>
void
SimpleIEW<Impl, IQ>::wakeDependents(DynInstPtr &inst)
{
    instQueue.wakeDependents(inst);
}

template<class Impl, class IQ>
void
SimpleIEW<Impl, IQ>::block()
{
    DPRINTF(IEW, "IEW: Blocking.\n");
    // Set the status to Blocked.
    _status = Blocked;

    // Add the current inputs to the skid buffer so they can be
    // reprocessed when this stage unblocks.
    skidBuffer.push(*fromRename);

    // Note that this stage only signals previous stages to stall when
    // it is the cause of the stall originates at this stage.  Otherwise
    // the previous stages are expected to check all possible stall signals.
}

template<class Impl, class IQ>
inline void
SimpleIEW<Impl, IQ>::unblock()
{
    // Check if there's information in the skid buffer.  If there is, then
    // set status to unblocking, otherwise set it directly to running.
    DPRINTF(IEW, "IEW: Reading instructions out of the skid "
            "buffer.\n");
    // Remove the now processed instructions from the skid buffer.
    skidBuffer.pop();

    // If there's still information in the skid buffer, then
    // continue to tell previous stages to stall.  They will be
    // able to restart once the skid buffer is empty.
    if (!skidBuffer.empty()) {
        toRename->iewInfo.stall = true;
    } else {
        DPRINTF(IEW, "IEW: Stage is done unblocking.\n");
        _status = Running;
    }
}

template<class Impl, class IQ>
void
SimpleIEW<Impl, IQ>::squash()
{
    DPRINTF(IEW, "IEW: Squashing all instructions.\n");
    _status = Squashing;

    // Tell the IQ to start squashing.
    instQueue.squash();

    // Tell the LDSTQ to start squashing.
    ldstQueue.squash(fromCommit->commitInfo.doneSeqNum);
}

template<class Impl, class IQ>
void
SimpleIEW<Impl, IQ>::squash(DynInstPtr &inst)
{
    DPRINTF(IEW, "IEW: Squashing from a specific instruction, PC: %#x.\n",
            inst->PC);
    // Perhaps leave the squashing up to the ROB stage to tell it when to
    // squash?
    _status = Squashing;

    // Tell rename to squash through the time buffer.
    toRename->iewInfo.squash = true;
    // Also send PC update information back to prior stages.
    toRename->iewInfo.squashedSeqNum = inst->seqNum;
    toRename->iewInfo.mispredPC = inst->readPC();
    toRename->iewInfo.nextPC = inst->readCalcTarg();
    toRename->iewInfo.branchMispredict = true;
    // Prediction was incorrect, so send back inverse.
    toRename->iewInfo.branchTaken = !(inst->predTaken());
}

template<class Impl, class IQ>
void
SimpleIEW<Impl, IQ>::tick()
{
    // Considering putting all the state-determining stuff in this section.

    // Try to fill up issue queue with as many instructions as bandwidth
    // allows.
    // Decode should try to execute as many instructions as its bandwidth
    // will allow, as long as it is not currently blocked.

    // Check if the stage is in a running status.
    if (_status != Blocked && _status != Squashing) {
        DPRINTF(IEW, "IEW: Status is not blocked, attempting to run "
                     "stage.\n");
        iew();

        // If it's currently unblocking, check to see if it should switch
        // to running.
        if (_status == Unblocking) {
            unblock();
        }
    } else if (_status == Squashing) {

        DPRINTF(IEW, "IEW: Still squashing.\n");

        // Check if stage should remain squashing.  Stop squashing if the
        // squash signal clears.
        if (!fromCommit->commitInfo.squash &&
            !fromCommit->commitInfo.robSquashing) {
            DPRINTF(IEW, "IEW: Done squashing, changing status to "
                    "running.\n");

            _status = Running;
            instQueue.stopSquash();
        } else {
            instQueue.doSquash();
        }

        // Also should advance its own time buffers if the stage ran.
        // Not sure about this...
//        issueToExecQueue.advance();
    } else if (_status == Blocked) {
        // Continue to tell previous stage to stall.
        toRename->iewInfo.stall = true;

        // Check if possible stall conditions have cleared.
        if (!fromCommit->commitInfo.stall &&
            !instQueue.isFull()) {
            DPRINTF(IEW, "IEW: Stall signals cleared, going to unblock.\n");
            _status = Unblocking;
        }

        // If there's still instructions coming from rename, continue to
        // put them on the skid buffer.
        if (fromRename->insts[0]) {
            block();
        }

        if (fromCommit->commitInfo.squash ||
            fromCommit->commitInfo.robSquashing) {
            squash();
        }
    }

    // @todo: Maybe put these at the beginning, so if it's idle it can
    // return early.
    // Write back number of free IQ entries here.
    toRename->iewInfo.freeIQEntries = instQueue.numFreeEntries();

    // Check the committed load/store signals to see if there's a load
    // or store to commit.  Also check if it's being told to execute a
    // nonspeculative instruction.
    if (fromCommit->commitInfo.commitIsStore) {
        ldstQueue.commitStores(fromCommit->commitInfo.doneSeqNum);
    } else if (fromCommit->commitInfo.commitIsLoad) {
        ldstQueue.commitLoads(fromCommit->commitInfo.doneSeqNum);
    }

    if (fromCommit->commitInfo.nonSpecSeqNum != 0) {
        instQueue.scheduleNonSpec(fromCommit->commitInfo.nonSpecSeqNum);
    }

    DPRINTF(IEW, "IEW: IQ has %i free entries.\n",
            instQueue.numFreeEntries());
}

template<class Impl, class IQ>
void
SimpleIEW<Impl, IQ>::iew()
{
    // Might want to put all state checks in the tick() function.
    // Check if being told to stall from commit.
    if (fromCommit->commitInfo.stall) {
        block();
        return;
    } else if (fromCommit->commitInfo.squash ||
               fromCommit->commitInfo.robSquashing) {
        // Also check if commit is telling this stage to squash.
        squash();
        return;
    }

    ////////////////////////////////////////
    // DISPATCH/ISSUE stage
    ////////////////////////////////////////

    //Put into its own function?
    //Add instructions to IQ if there are any instructions there

    // Check if there are any instructions coming from rename, and we're.
    // not squashing.
    if (fromRename->insts[0] && _status != Squashing) {

        // Loop through the instructions, putting them in the instruction
        // queue.
        for (int inst_num = 0; inst_num < issueReadWidth; ++inst_num)
        {
            DynInstPtr inst = fromRename->insts[inst_num];

            // Make sure there's a valid instruction there.
            if (!inst)
                break;

            DPRINTF(IEW, "IEW: Issue: Adding PC %#x to IQ.\n",
                    inst->readPC());

            // If it's a memory reference, don't put it in the
            // instruction queue.  These will only be executed at commit.
            // Do the same for nonspeculative instructions and nops.
            // Be sure to mark these instructions as ready so that the
            // commit stage can go ahead and execute them, and mark
            // them as issued so the IQ doesn't reprocess them.
            if (inst->isSquashed()) {
                continue;
            } else if (inst->isLoad()) {
                DPRINTF(IEW, "IEW: Issue: Memory instruction "
                        "encountered, adding to LDSTQ.\n");

                // Reserve a spot in the load store queue for this
                // memory access.
                ldstQueue.insertLoad(inst);

            } else if (inst->isStore()) {
                ldstQueue.insertStore(inst);

                // A bit of a hack.  Set that it can commit so that
                // the commit stage will try committing it, and then
                // once commit realizes it's a store it will send back
                // a signal to this stage to issue and execute that
                // store.
                inst->setCanCommit();

                instQueue.insertNonSpec(inst);
                continue;
            } else if (inst->isNonSpeculative()) {
                DPRINTF(IEW, "IEW: Issue: Nonspeculative instruction "
                        "encountered, skipping.\n");

                // Same hack as with stores.
                inst->setCanCommit();

                // Specificall insert it as nonspeculative.
                instQueue.insertNonSpec(inst);

                continue;
            } else if (inst->isNop()) {
                DPRINTF(IEW, "IEW: Issue: Nop instruction encountered "
                        ", skipping.\n");

                inst->setIssued();
                inst->setExecuted();
                inst->setCanCommit();

                instQueue.advanceTail(inst);
                continue;
            } else if (instQueue.isFull()) {
                DPRINTF(IEW, "IEW: Issue: IQ has become full.\n");
                // Call function to start blocking.
                block();
                // Tell previous stage to stall.
                toRename->iewInfo.stall = true;
                break;
            }

            // If the instruction queue is not full, then add the
            // instruction.
            instQueue.insert(fromRename->insts[inst_num]);
        }
    }

    // Have the instruction queue try to schedule any ready instructions.
    instQueue.scheduleReadyInsts();

    ////////////////////////////////////////
    //EXECUTE/WRITEBACK stage
    ////////////////////////////////////////

    //Put into its own function?
    //Similarly should probably have separate execution for int vs FP.
    // Above comment is handled by the issue queue only issuing a valid
    // mix of int/fp instructions.
    //Actually okay to just have one execution, buuuuuut will need
    //somewhere that defines the execution latency of all instructions.
    // @todo: Move to the FU pool used in the current full cpu.

    int fu_usage = 0;
    bool fetch_redirect = false;

    // Execute/writeback any instructions that are available.
    for (int inst_num = 0;
         fu_usage < executeWidth && /* Haven't exceeded available FU's. */
         inst_num < issueWidth && /* Haven't exceeded issue width. */
         fromIssue->insts[inst_num]; /* There are available instructions. */
         ++inst_num) {
        DPRINTF(IEW, "IEW: Execute: Executing instructions from IQ.\n");

        // Get instruction from issue's queue.
        DynInstPtr inst = fromIssue->insts[inst_num];

        DPRINTF(IEW, "IEW: Execute: Processing PC %#x.\n", inst->readPC());

        // Check if the instruction is squashed; if so then skip it
        // and don't count it towards the FU usage.
        if (inst->isSquashed()) {
            DPRINTF(IEW, "IEW: Execute: Instruction was squashed.\n");

            // Consider this instruction executed so that commit can go
            // ahead and retire the instruction.
            inst->setExecuted();

            toCommit->insts[inst_num] = inst;

            continue;
        }

        inst->setExecuted();

        // If an instruction is executed, then count it towards FU usage.
        ++fu_usage;

        // Execute instruction.
        // Note that if the instruction faults, it will be handled
        // at the commit stage.
        if (inst->isMemRef()) {
            DPRINTF(IEW, "IEW: Execute: Calculating address for memory "
                    "reference.\n");

            // Tell the LDSTQ to execute this instruction (if it is a load).
            if (inst->isLoad()) {
                ldstQueue.executeLoad(inst);
            } else if (inst->isStore()) {
                ldstQueue.executeStore();
            } else {
                panic("IEW: Unexpected memory type!\n");
            }

        } else {
            inst->execute();
        }

        // First check the time slot that this instruction will write
        // to.  If there are free write ports at the time, then go ahead
        // and write the instruction to that time.  If there are not,
        // keep looking back to see where's the first time there's a
        // free slot.  What happens if you run out of free spaces?
        // For now naively assume that all instructions take one cycle.
        // Otherwise would have to look into the time buffer based on the
        // latency of the instruction.

        // Add finished instruction to queue to commit.
        toCommit->insts[inst_num] = inst;

        // Check if branch was correct.  This check happens after the
        // instruction is added to the queue because even if the branch
        // is mispredicted, the branch instruction itself is still valid.
        // Only handle this if there hasn't already been something that
        // redirects fetch in this group of instructions.
        if (!fetch_redirect) {
            if (inst->mispredicted()) {
                fetch_redirect = true;

                DPRINTF(IEW, "IEW: Execute: Branch mispredict detected.\n");
                DPRINTF(IEW, "IEW: Execute: Redirecting fetch to PC: %#x.\n",
                        inst->nextPC);

                // If incorrect, then signal the ROB that it must be squashed.
                squash(inst);
            } else if (ldstQueue.violation()) {
                fetch_redirect = true;

                DynInstPtr violator = ldstQueue.getMemDepViolator();

                DPRINTF(IEW, "IEW: LDSTQ detected a violation.  Violator PC: "
                        "%#x, inst PC: %#x.  Addr is: %#x.\n",
                        violator->readPC(), inst->readPC(), inst->physEffAddr);

                instQueue.violation(inst, violator);

                squash(inst);
                // Otherwise check if there was a memory ordering violation.
                // If there was, then signal ROB that it must be squashed.  Also
                // signal IQ that there was a violation.
            }
        }
    }

    // Loop through the head of the time buffer and wake any dependents.
    // These instructions are about to write back.  In the simple model
    // this loop can really happen within the previous loop, but when
    // instructions have actual latencies, this loop must be separate.
    // Also mark scoreboard that this instruction is finally complete.
    // Either have IEW have direct access to rename map, or have this as
    // part of backwards communication.
    for (int inst_num = 0; inst_num < executeWidth &&
             toCommit->insts[inst_num]; inst_num++)
    {
        DynInstPtr inst = toCommit->insts[inst_num];

        DPRINTF(IEW, "IEW: Sending instructions to commit, PC %#x.\n",
                inst->readPC());

        if(!inst->isSquashed()) {
            instQueue.wakeDependents(inst);

            for (int i = 0; i < inst->numDestRegs(); i++)
            {
                renameMap->markAsReady(inst->renamedDestRegIdx(i));
            }
        }
    }

    // Also should advance its own time buffers if the stage ran.
    // Not the best place for it, but this works (hopefully).
    issueToExecQueue.advance();
}