Page principale | Hiérarchie des classes | Liste des classes | Répertoires | Liste des fichiers | Membres de classe | Membres de fichier

interrupt.cc

Aller à la documentation de ce fichier.
00001 // interrupt.cc 
00002 //      Routines to simulate hardware interrupts.
00003 //
00004 //      The hardware provides a routine (SetLevel) to enable or disable
00005 //      interrupts.
00006 //
00007 //      In order to emulate the hardware, we need to keep track of all
00008 //      interrupts the hardware devices would cause, and when they
00009 //      are supposed to occur.  
00010 //
00011 //      This module also keeps track of simulated time.  Time advances
00012 //      only when the following occur: 
00013 //              interrupts are re-enabled
00014 //              a user instruction is executed
00015 //              there is nothing in the ready queue
00016 //
00017 //  DO NOT CHANGE -- part of the machine emulation
00018 //
00019 // Copyright (c) 1992-1996 The Regents of the University of California.
00020 // All rights reserved.  See copyright.h for copyright notice and limitation 
00021 // of liability and disclaimer of warranty provisions.
00022 
00023 #include "copyright.h"
00024 #include "interrupt.h"
00025 #include "main.h"
00026 
00027 // String definitions for debugging messages
00028 
00029 static char *intLevelNames[] = { "off", "on"};
00030 static char *intTypeNames[] = { "timer", "disk", "console write", 
00031                         "console read", "network send", 
00032                         "network recv"};
00033 
00034 //----------------------------------------------------------------------
00035 // PendingInterrupt::PendingInterrupt
00036 //      Initialize a hardware device interrupt that is to be scheduled 
00037 //      to occur in the near future.
00038 //
00039 //      "callOnInt" is the object to call when the interrupt occurs
00040 //      "time" is when (in simulated time) the interrupt is to occur
00041 //      "kind" is the hardware device that generated the interrupt
00042 //----------------------------------------------------------------------
00043 
00044 PendingInterrupt::PendingInterrupt(CallBackObj *callOnInt, 
00045                                         int time, IntType kind)
00046 {
00047     callOnInterrupt = callOnInt;
00048     when = time;
00049     type = kind;
00050 }
00051 
00052 //----------------------------------------------------------------------
00053 // PendingCompare
00054 //      Compare to interrupts based on which should occur first.
00055 //----------------------------------------------------------------------
00056 
00057 static int
00058 PendingCompare (PendingInterrupt *x, PendingInterrupt *y)
00059 {
00060     if (x->when < y->when) { return -1; }
00061     else if (x->when > y->when) { return 1; }
00062     else { return 0; }
00063 }
00064 
00065 //----------------------------------------------------------------------
00066 // Interrupt::Interrupt
00067 //      Initialize the simulation of hardware device interrupts.
00068 //      
00069 //      Interrupts start disabled, with no interrupts pending, etc.
00070 //----------------------------------------------------------------------
00071 
00072 Interrupt::Interrupt()
00073 {
00074     level = IntOff;
00075     pending = new SortedList<PendingInterrupt *>(PendingCompare);
00076     inHandler = FALSE;
00077     yieldOnReturn = FALSE;
00078     status = SystemMode;
00079 }
00080 
00081 //----------------------------------------------------------------------
00082 // Interrupt::~Interrupt
00083 //      De-allocate the data structures needed by the interrupt simulation.
00084 //----------------------------------------------------------------------
00085 
00086 Interrupt::~Interrupt()
00087 {
00088     while (!pending->IsEmpty()) {
00089         delete pending->RemoveFront();
00090     }
00091     delete pending;
00092 }
00093 
00094 //----------------------------------------------------------------------
00095 // Interrupt::ChangeLevel
00096 //      Change interrupts to be enabled or disabled, without advancing 
00097 //      the simulated time (normally, enabling interrupts advances the time).
00098 //
00099 //      Used internally.
00100 //
00101 //      "old" -- the old interrupt status
00102 //      "now" -- the new interrupt status
00103 //----------------------------------------------------------------------
00104 
00105 void
00106 Interrupt::ChangeLevel(IntStatus old, IntStatus now)
00107 {
00108     level = now;
00109     DEBUG(dbgInt, "\tinterrupts: " << intLevelNames[old] << " -> " << intLevelNames[now]);
00110 }
00111 
00112 //----------------------------------------------------------------------
00113 // Interrupt::SetLevel
00114 //      Change interrupts to be enabled or disabled, and if interrupts
00115 //      are being enabled, advance simulated time by calling OneTick().
00116 //
00117 // Returns:
00118 //      The old interrupt status.
00119 // Parameters:
00120 //      "now" -- the new interrupt status
00121 //----------------------------------------------------------------------
00122 
00123 IntStatus
00124 Interrupt::SetLevel(IntStatus now)
00125 {
00126     IntStatus old = level;
00127     
00128     // interrupt handlers are prohibited from enabling interrupts
00129     ASSERT((now == IntOff) || (inHandler == FALSE));
00130 
00131     ChangeLevel(old, now);                      // change to new state
00132     if ((now == IntOn) && (old == IntOff)) {
00133         OneTick();                              // advance simulated time
00134     }
00135     return old;
00136 }
00137 
00138 //----------------------------------------------------------------------
00139 // Interrupt::OneTick
00140 //      Advance simulated time and check if there are any pending 
00141 //      interrupts to be called. 
00142 //
00143 //      Two things can cause OneTick to be called:
00144 //              interrupts are re-enabled
00145 //              a user instruction is executed
00146 //----------------------------------------------------------------------
00147 void
00148 Interrupt::OneTick()
00149 {
00150     MachineStatus oldStatus = status;
00151     Statistics *stats = kernel->stats;
00152 
00153 // advance simulated time
00154     if (status == SystemMode) {
00155         stats->totalTicks += SystemTick;
00156         stats->systemTicks += SystemTick;
00157     } else {
00158         stats->totalTicks += UserTick;
00159         stats->userTicks += UserTick;
00160     }
00161     DEBUG(dbgInt, "== Tick " << stats->totalTicks << " ==");
00162 
00163 // check any pending interrupts are now ready to fire
00164     ChangeLevel(IntOn, IntOff); // first, turn off interrupts
00165                                 // (interrupt handlers run with
00166                                 // interrupts disabled)
00167     CheckIfDue(FALSE);          // check for pending interrupts
00168     ChangeLevel(IntOff, IntOn); // re-enable interrupts
00169     if (yieldOnReturn) {        // if the timer device handler asked 
00170                                 // for a context switch, ok to do it now
00171         yieldOnReturn = FALSE;
00172         status = SystemMode;            // yield is a kernel routine
00173         kernel->currentThread->Yield();
00174         status = oldStatus;
00175     }
00176 }
00177 
00178 //----------------------------------------------------------------------
00179 // Interrupt::YieldOnReturn
00180 //      Called from within an interrupt handler, to cause a context switch
00181 //      (for example, on a time slice) in the interrupted thread,
00182 //      when the handler returns.
00183 //
00184 //      We can't do the context switch here, because that would switch
00185 //      out the interrupt handler, and we want to switch out the 
00186 //      interrupted thread.
00187 //----------------------------------------------------------------------
00188 
00189 void
00190 Interrupt::YieldOnReturn()
00191 { 
00192     ASSERT(inHandler == TRUE);  
00193     yieldOnReturn = TRUE; 
00194 }
00195 
00196 //----------------------------------------------------------------------
00197 // Interrupt::Idle
00198 //      Routine called when there is nothing in the ready queue.
00199 //
00200 //      Since something has to be running in order to put a thread
00201 //      on the ready queue, the only thing to do is to advance 
00202 //      simulated time until the next scheduled hardware interrupt.
00203 //
00204 //      If there are no pending interrupts, stop.  There's nothing
00205 //      more for us to do.
00206 //----------------------------------------------------------------------
00207 void
00208 Interrupt::Idle()
00209 {
00210     DEBUG(dbgInt, "Machine idling; checking for interrupts.");
00211     status = IdleMode;
00212     if (CheckIfDue(TRUE)) {     // check for any pending interrupts
00213         status = SystemMode;
00214         return;                 // return in case there's now
00215                                 // a runnable thread
00216     }
00217 
00218     // if there are no pending interrupts, and nothing is on the ready
00219     // queue, it is time to stop.   If the console or the network is 
00220     // operating, there are *always* pending interrupts, so this code
00221     // is not reached.  Instead, the halt must be invoked by the user program.
00222 
00223     DEBUG(dbgInt, "Machine idle.  No interrupts to do.");
00224     cout << "No threads ready or runnable, and no pending interrupts.\n";
00225     cout << "Assuming the program completed.\n";
00226     Halt();
00227 }
00228 
00229 //----------------------------------------------------------------------
00230 // Interrupt::Halt
00231 //      Shut down Nachos cleanly, printing out performance statistics.
00232 //----------------------------------------------------------------------
00233 void
00234 Interrupt::Halt()
00235 {
00236     cerr << "Machine halting!\n\n";
00237     kernel->stats->Print();
00238     delete kernel;      // Never returns.
00239 }
00240 
00241 //----------------------------------------------------------------------
00242 // Interrupt::Schedule
00243 //      Arrange for the CPU to be interrupted when simulated time
00244 //      reaches "now + when".
00245 //
00246 //      Implementation: just put it on a sorted list.
00247 //
00248 //      NOTE: the Nachos kernel should not call this routine directly.
00249 //      Instead, it is only called by the hardware device simulators.
00250 //
00251 //      "toCall" is the object to call when the interrupt occurs
00252 //      "fromNow" is how far in the future (in simulated time) the 
00253 //               interrupt is to occur
00254 //      "type" is the hardware device that generated the interrupt
00255 //----------------------------------------------------------------------
00256 void
00257 Interrupt::Schedule(CallBackObj *toCall, int fromNow, IntType type)
00258 {
00259     int when = kernel->stats->totalTicks + fromNow;
00260     PendingInterrupt *toOccur = new PendingInterrupt(toCall, when, type);
00261 
00262     DEBUG(dbgInt, "Scheduling interrupt handler the " << intTypeNames[type] << " at time = " << when);
00263     ASSERT(fromNow > 0);
00264 
00265     pending->Insert(toOccur);
00266 }
00267 
00268 //----------------------------------------------------------------------
00269 // Interrupt::CheckIfDue
00270 //      Check if any interrupts are scheduled to occur, and if so, 
00271 //      fire them off.
00272 //
00273 // Returns:
00274 //      TRUE, if we fired off any interrupt handlers
00275 // Params:
00276 //      "advanceClock" -- if TRUE, there is nothing in the ready queue,
00277 //              so we should simply advance the clock to when the next 
00278 //              pending interrupt would occur (if any).
00279 //----------------------------------------------------------------------
00280 bool
00281 Interrupt::CheckIfDue(bool advanceClock)
00282 {
00283     PendingInterrupt *next;
00284     Statistics *stats = kernel->stats;
00285 
00286     ASSERT(level == IntOff);            // interrupts need to be disabled,
00287                                         // to invoke an interrupt handler
00288     if (debug->IsEnabled(dbgInt)) {
00289         DumpState();
00290     }
00291     if (pending->IsEmpty()) {           // no pending interrupts
00292         return FALSE;   
00293     }           
00294     next = pending->Front();
00295     if (next->when > stats->totalTicks) {
00296         if (!advanceClock) {            // not time yet
00297             return FALSE;
00298         }
00299         else {                  // advance the clock to next interrupt
00300             stats->idleTicks += (next->when - stats->totalTicks);
00301             stats->totalTicks = next->when;
00302             // UDelay(1000L); // rcgood - to stop nachos from spinning.
00303         }
00304     }
00305 
00306     DEBUG(dbgInt, "Invoking interrupt handler for the ");
00307     DEBUG(dbgInt, intTypeNames[next->type] << " at time " << next->when);
00308 
00309     if (kernel->machine != NULL) {
00310         kernel->machine->DelayedLoad(0, 0);
00311     }
00312 
00313     inHandler = TRUE;
00314     do {
00315         next = pending->RemoveFront();    // pull interrupt off list
00316         next->callOnInterrupt->CallBack();// call the interrupt handler
00317         delete next;
00318     } while (!pending->IsEmpty() 
00319                 && (pending->Front()->when <= stats->totalTicks));
00320     inHandler = FALSE;
00321     return TRUE;
00322 }
00323 
00324 //----------------------------------------------------------------------
00325 // PrintPending
00326 //      Print information about an interrupt that is scheduled to occur.
00327 //      When, where, why, etc.
00328 //----------------------------------------------------------------------
00329 
00330 static void
00331 PrintPending (PendingInterrupt *pending)
00332 {
00333     cerr << "Interrupt handler "<< intTypeNames[pending->type];
00334     cerr << ", scheduled at " << pending->when;
00335 }
00336 
00337 //----------------------------------------------------------------------
00338 // DumpState
00339 //      Print the complete interrupt state - the status, and all interrupts
00340 //      that are scheduled to occur in the future.
00341 //----------------------------------------------------------------------
00342 
00343 void
00344 Interrupt::DumpState()
00345 {
00346     cerr << "Time: " << kernel->stats->totalTicks;
00347     cerr << ", interrupts " << intLevelNames[level] << "\n";
00348     cerr << "Pending interrupts:\n";
00349     pending->Apply(PrintPending);
00350     cerr << "\nEnd of pending interrupts\n";
00351 }

Généré le Sun Jan 15 00:44:24 2006 pour Architecture Cible de NachOS : par  doxygen 1.4.4