Post

Gem5 Interrupt Handling O3

Interrupt makes the commit stage stop further instructions fetching

1
2
3
4
5
6
7
8
9
 813 template <class Impl>
 814 void
 815 DefaultCommit<Impl>::commit()
 816 {
 817     if (FullSystem) {
 818         // Check if we have a interrupt and get read to handle it
 819         if (cpu->checkInterrupts(cpu->tcBase(0)))
 820             propagateInterrupt();
 821     }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 788 template <class Impl>
 789 void
 790 DefaultCommit<Impl>::propagateInterrupt()
 791 {
 792     // Don't propagate intterupts if we are currently handling a trap or
 793     // in draining and the last observable instruction has been committed.
 794     if (commitStatus[0] == TrapPending || interrupt || trapSquash[0] ||
 795             tcSquash[0] || drainImminent)
 796         return;
 797 
 798     // Process interrupts if interrupts are enabled, not in PAL
 799     // mode, and no other traps or external squashes are currently
 800     // pending.
 801     // @todo: Allow other threads to handle interrupts.
 802 
 803     // Get any interrupt that happened
 804     interrupt = cpu->getInterrupts();
 805 
 806     // Tell fetch that there is an interrupt pending.  This
 807     // will make fetch wait until it sees a non PAL-mode PC,
 808     // at which point it stops fetching instructions.
 809     if (interrupt != NoFault)
 810         toIEW->commitInfo[0].interruptPending = true;
 811 }

If the commit stage found that the interrupt needs to be handled, through the getInterrupts function, it should first send signal to IEW stage and prevent further instruction fetching until the interrupt is resolved.

Handle interrupt at the time of instruction commit

After commit stage checks possible squash on the threads and presence of interrupt, it tries to commit the instructions.

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
 976 template <class Impl>
 977 void
 978 DefaultCommit<Impl>::commitInsts()
 979 {
 980     ////////////////////////////////////
 981     // Handle commit
 982     // Note that commit will be handled prior to putting new
 983     // instructions in the ROB so that the ROB only tries to commit
 984     // instructions it has in this current cycle, and not instructions
 985     // it is writing in during this cycle.  Can't commit and squash
 986     // things at the same time...
 987     ////////////////////////////////////
 988 
 989     DPRINTF(Commit, "Trying to commit instructions in the ROB.\n");
 990 
 991     unsigned num_committed = 0;
 992 
 993     DynInstPtr head_inst;
 994 
 995     // Commit as many instructions as possible until the commit bandwidth
 996     // limit is reached, or it becomes impossible to commit any more.
 997     while (num_committed < commitWidth) {
 998         // Check for any interrupt that we've already squashed for
 999         // and start processing it.
1000         if (interrupt != NoFault)
1001             handleInterrupt();

If there were pending interrupt, the instructions cannot be committed.

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
 734 template <class Impl>
 735 void
 736 DefaultCommit<Impl>::handleInterrupt()
 737 {
 738     // Verify that we still have an interrupt to handle
 739     if (!cpu->checkInterrupts(cpu->tcBase(0))) {
 740         DPRINTF(Commit, "Pending interrupt is cleared by master before "
 741                 "it got handled. Restart fetching from the orig path.\n");
 742         toIEW->commitInfo[0].clearInterrupt = true;
 743         interrupt = NoFault;
 744         avoidQuiesceLiveLock = true;
 745         return;
 746     }
 747 
 748     // Wait until all in flight instructions are finished before enterring
 749     // the interrupt.
 750     if (canHandleInterrupts && cpu->instList.empty()) {
 751         // Squash or record that I need to squash this cycle if
 752         // an interrupt needed to be handled.
 753         DPRINTF(Commit, "Interrupt detected.\n");
 754 
 755         // Clear the interrupt now that it's going to be handled
 756         toIEW->commitInfo[0].clearInterrupt = true;
 757 
 758         assert(!thread[0]->noSquashFromTC);
 759         thread[0]->noSquashFromTC = true;
 760 
 761         if (cpu->checker) {
 762             cpu->checker->handlePendingInt();
 763         }
 764 
 765         // CPU will handle interrupt. Note that we ignore the local copy of
 766         // interrupt. This is because the local copy may no longer be the
 767         // interrupt that the interrupt controller thinks is being handled.
 768         cpu->processInterrupts(cpu->getInterrupts());
 769 
 770         thread[0]->noSquashFromTC = false;
 771 
 772         commitStatus[0] = TrapPending;
 773 
 774         interrupt = NoFault;
 775 
 776         // Generate trap squash event.
 777         generateTrapEvent(0, interrupt);
 778 
 779         avoidQuiesceLiveLock = false;
 780     } else {
 781         DPRINTF(Commit, "Interrupt pending: instruction is %sin "
 782                 "flight, ROB is %sempty\n",
 783                 canHandleInterrupts ? "not " : "",
 784                 cpu->instList.empty() ? "" : "not " );
 785     }
 786 }

Architecture dependent interrupt checking

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
614 bool
615 X86ISA::Interrupts::checkInterrupts(ThreadContext *tc) const
616 {
617     RFLAGS rflags = tc->readMiscRegNoEffect(MISCREG_RFLAGS);
618     if (pendingUnmaskableInt) {
619         DPRINTF(LocalApic, "Reported pending unmaskable interrupt.\n");
620         return true;
621     }
622     if (rflags.intf) {
623         if (pendingExtInt) {
624             DPRINTF(LocalApic, "Reported pending external interrupt.\n");
625             return true;
626         }
627         if (IRRV > ISRV && bits(IRRV, 7, 4) >
628                bits(regs[APIC_TASK_PRIORITY], 7, 4)) {
629             DPRINTF(LocalApic, "Reported pending regular interrupt.\n");
630             return true;
631         }
632     }
633     return false;
634 }

Processing interrupt through invoke

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
 886 template <class Impl>
 887 void    
 888 FullO3CPU<Impl>::processInterrupts(const Fault &interrupt)
 889 {       
 890     // Check for interrupts here.  For now can copy the code that
 891     // exists within isa_fullsys_traits.hh.  Also assume that thread 0
 892     // is the one that handles the interrupts.
 893     // @todo: Possibly consolidate the interrupt checking code.
 894     // @todo: Allow other threads to handle interrupts.
 895         
 896     assert(interrupt != NoFault);
 897     this->interrupts[0]->updateIntrInfo(this->threadContexts[0]);
 898         
 899     DPRINTF(O3CPU, "Interrupt %s being handled\n", interrupt->name());
 900     this->trap(interrupt, 0, nullptr);
 901 }       
 902
 903 template <class Impl>
 904 void
 905 FullO3CPU<Impl>::trap(const Fault &fault, ThreadID tid,
 906                       const StaticInstPtr &inst)
 907 {       
 908     // Pass the thread's TC into the invoke method.
 909     fault->invoke(this->threadContexts[tid], inst);
 910 }      

Generate trap event

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 526 template <class Impl>
 527 void
 528 DefaultCommit<Impl>::generateTrapEvent(ThreadID tid, Fault inst_fault)
 529 {
 530     DPRINTF(Commit, "Generating trap event for [tid:%i]\n", tid);
 531 
 532     EventFunctionWrapper *trap = new EventFunctionWrapper(
 533         [this, tid]{ processTrapEvent(tid); },
 534         "Trap", true, Event::CPU_Tick_Pri);
 535 
 536     Cycles latency = dynamic_pointer_cast<SyscallRetryFault>(inst_fault) ?
 537                      cpu->syscallRetryLatency : trapLatency;
 538 
 539     cpu->schedule(trap, cpu->clockEdge(latency));
 540     trapInFlight[tid] = true;
 541     thread[tid]->trapPending = true;
 542 }
This post is licensed under CC BY 4.0 by the author.

Comments powered by Disqus.