Main Page   Compound List   File List   Compound Members   File Members  

interrupt.cc

Go to the documentation of this file.
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-1993 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 "system.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", "network recv"};
00032 
00033 //----------------------------------------------------------------------
00034 // PendingInterrupt::PendingInterrupt
00035 //      Initialize a hardware device interrupt that is to be scheduled 
00036 //      to occur in the near future.
00037 //
00038 //      "func" is the procedure to call when the interrupt occurs
00039 //      "param" is the argument to pass to the procedure
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(VoidFunctionPtr func, int param, int time, 
00045                                 IntType kind)
00046 {
00047     handler = func;
00048     arg = param;
00049     when = time;
00050     type = kind;
00051 }
00052 
00053 //----------------------------------------------------------------------
00054 // Interrupt::Interrupt
00055 //      Initialize the simulation of hardware device interrupts.
00056 //      
00057 //      Interrupts start disabled, with no interrupts pending, etc.
00058 //----------------------------------------------------------------------
00059 
00060 Interrupt::Interrupt()
00061 {
00062     level = IntOff;
00063     pending = new List();
00064     inHandler = FALSE;
00065     yieldOnReturn = FALSE;
00066     status = SystemMode;
00067 }
00068 
00069 //----------------------------------------------------------------------
00070 // Interrupt::~Interrupt
00071 //      De-allocate the data structures needed by the interrupt simulation.
00072 //----------------------------------------------------------------------
00073 
00074 Interrupt::~Interrupt()
00075 {
00076     while (!pending->IsEmpty())
00077         delete pending->Remove();
00078     delete pending;
00079 }
00080 
00081 //----------------------------------------------------------------------
00082 // Interrupt::ChangeLevel
00083 //      Change interrupts to be enabled or disabled, without advancing 
00084 //      the simulated time (normally, enabling interrupts advances the time).
00085 
00086 //----------------------------------------------------------------------
00087 // Interrupt::ChangeLevel
00088 //      Change interrupts to be enabled or disabled, without advancing 
00089 //      the simulated time (normally, enabling interrupts advances the time).
00090 //
00091 //      Used internally.
00092 //
00093 //      "old" -- the old interrupt status
00094 //      "now" -- the new interrupt status
00095 //----------------------------------------------------------------------
00096 void
00097 Interrupt::ChangeLevel(IntStatus old, IntStatus now)
00098 {
00099     level = now;
00100     DEBUG('i',"\tinterrupts: %s -> %s\n",intLevelNames[old],intLevelNames[now]);
00101 }
00102 
00103 //----------------------------------------------------------------------
00104 // Interrupt::SetLevel
00105 //      Change interrupts to be enabled or disabled, and if interrupts
00106 //      are being enabled, advance simulated time by calling OneTick().
00107 //
00108 // Returns:
00109 //      The old interrupt status.
00110 // Parameters:
00111 //      "now" -- the new interrupt status
00112 //----------------------------------------------------------------------
00113 
00114 IntStatus
00115 Interrupt::SetLevel(IntStatus now)
00116 {
00117     IntStatus old = level;
00118     
00119     ASSERT((now == IntOff) || (inHandler == FALSE));// interrupt handlers are 
00120                                                 // prohibited from enabling 
00121                                                 // interrupts
00122 
00123     ChangeLevel(old, now);                      // change to new state
00124     if ((now == IntOn) && (old == IntOff))
00125         OneTick();                              // advance simulated time
00126     return old;
00127 }
00128 
00129 //----------------------------------------------------------------------
00130 // Interrupt::Enable
00131 //      Turn interrupts on.  Who cares what they used to be? 
00132 //      Used in ThreadRoot, to turn interrupts on when first starting up
00133 //      a thread.
00134 //----------------------------------------------------------------------
00135 void
00136 Interrupt::Enable()
00137 { 
00138     (void) SetLevel(IntOn); 
00139 }
00140 
00141 //----------------------------------------------------------------------
00142 // Interrupt::OneTick
00143 //      Advance simulated time and check if there are any pending 
00144 //      interrupts to be called. 
00145 //
00146 //      Two things can cause OneTick to be called:
00147 //              interrupts are re-enabled
00148 //              a user instruction is executed
00149 //----------------------------------------------------------------------
00150 void
00151 Interrupt::OneTick()
00152 {
00153     MachineStatus old = status;
00154 
00155 // advance simulated time
00156     if (status == SystemMode) {
00157         stats->totalTicks += SystemTick;
00158         stats->systemTicks += SystemTick;
00159     } else {                                    // USER_PROGRAM
00160         stats->totalTicks += UserTick;
00161         stats->userTicks += UserTick;
00162     }
00163     DEBUG('i', "\n== Tick %d ==\n", stats->totalTicks);
00164 
00165 // check any pending interrupts are now ready to fire
00166     ChangeLevel(IntOn, IntOff);         // first, turn off interrupts
00167                                         // (interrupt handlers run with
00168                                         // interrupts disabled)
00169     while (CheckIfDue(FALSE))           // check for pending interrupts
00170         ;
00171     ChangeLevel(IntOff, IntOn);         // re-enable interrupts
00172     if (yieldOnReturn) {                // if the timer device handler asked 
00173                                         // for a context switch, ok to do it now
00174         yieldOnReturn = FALSE;
00175         status = SystemMode;            // yield is a kernel routine
00176         currentThread->Yield();
00177         status = old;
00178     }
00179 }
00180 
00181 //----------------------------------------------------------------------
00182 // Interrupt::YieldOnReturn
00183 //      Called from within an interrupt handler, to cause a context switch
00184 //      (for example, on a time slice) in the interrupted thread,
00185 //      when the handler returns.
00186 //
00187 //      We can't do the context switch here, because that would switch
00188 //      out the interrupt handler, and we want to switch out the 
00189 //      interrupted thread.
00190 //----------------------------------------------------------------------
00191 
00192 void
00193 Interrupt::YieldOnReturn()
00194 { 
00195     ASSERT(inHandler == TRUE);  
00196     yieldOnReturn = TRUE; 
00197 }
00198 
00199 //----------------------------------------------------------------------
00200 // Interrupt::Idle
00201 //      Routine called when there is nothing in the ready queue.
00202 //
00203 //      Since something has to be running in order to put a thread
00204 //      on the ready queue, the only thing to do is to advance 
00205 //      simulated time until the next scheduled hardware interrupt.
00206 //
00207 //      If there are no pending interrupts, stop.  There's nothing
00208 //      more for us to do.
00209 //----------------------------------------------------------------------
00210 void
00211 Interrupt::Idle()
00212 {
00213     DEBUG('i', "Machine idling; checking for interrupts.\n");
00214     status = IdleMode;
00215     if (CheckIfDue(TRUE)) {             // check for any pending interrupts
00216         while (CheckIfDue(FALSE))       // check for any other pending 
00217             ;                           // interrupts
00218         yieldOnReturn = FALSE;          // since there's nothing in the
00219                                         // ready queue, the yield is automatic
00220         status = SystemMode;
00221         return;                         // return in case there's now
00222                                         // a runnable thread
00223     }
00224 
00225     // if there are no pending interrupts, and nothing is on the ready
00226     // queue, it is time to stop.   If the console or the network is 
00227     // operating, there are *always* pending interrupts, so this code
00228     // is not reached.  Instead, the halt must be invoked by the user program.
00229 
00230     DEBUG('i', "Machine idle.  No interrupts to do.\n");
00231     printf("No threads ready or runnable, and no pending interrupts.\n");
00232     printf("Assuming the program completed.\n");
00233     Halt();
00234 }
00235 
00236 //----------------------------------------------------------------------
00237 // Interrupt::Halt
00238 //      Shut down Nachos cleanly, printing out performance statistics.
00239 //----------------------------------------------------------------------
00240 void
00241 Interrupt::Halt()
00242 {
00243     printf("Machine halting!\n\n");
00244     stats->Print();
00245     Cleanup();     // Never returns.
00246 }
00247 
00248 //----------------------------------------------------------------------
00249 // Interrupt::Schedule
00250 //      Arrange for the CPU to be interrupted when simulated time
00251 //      reaches "now + when".
00252 //
00253 //      Implementation: just put it on a sorted list.
00254 //
00255 //      NOTE: the Nachos kernel should not call this routine directly.
00256 //      Instead, it is only called by the hardware device simulators.
00257 //
00258 //      "handler" is the procedure to call when the interrupt occurs
00259 //      "arg" is the argument to pass to the procedure
00260 //      "fromNow" is how far in the future (in simulated time) the 
00261 //               interrupt is to occur
00262 //      "type" is the hardware device that generated the interrupt
00263 //----------------------------------------------------------------------
00264 void
00265 Interrupt::Schedule(VoidFunctionPtr handler, int arg, int fromNow, IntType type)
00266 {
00267     int when = stats->totalTicks + fromNow;
00268     PendingInterrupt *toOccur = new PendingInterrupt(handler, arg, when, type);
00269 
00270     DEBUG('i', "Scheduling interrupt handler the %s at time = %d\n", 
00271                                         intTypeNames[type], when);
00272     ASSERT(fromNow > 0);
00273 
00274     pending->SortedInsert(toOccur, when);
00275 }
00276 
00277 //----------------------------------------------------------------------
00278 // Interrupt::CheckIfDue
00279 //      Check if an interrupt is scheduled to occur, and if so, fire it off.
00280 //
00281 // Returns:
00282 //      TRUE, if we fired off any interrupt handlers
00283 // Params:
00284 //      "advanceClock" -- if TRUE, there is nothing in the ready queue,
00285 //              so we should simply advance the clock to when the next 
00286 //              pending interrupt would occur (if any).  If the pending
00287 //              interrupt is just the time-slice daemon, however, then 
00288 //              we're done!
00289 //----------------------------------------------------------------------
00290 bool
00291 Interrupt::CheckIfDue(bool advanceClock)
00292 {
00293     MachineStatus old = status;
00294     int when;
00295 
00296     ASSERT(level == IntOff);            // interrupts need to be disabled,
00297                                         // to invoke an interrupt handler
00298     if (DebugIsEnabled('i'))
00299         DumpState();
00300     PendingInterrupt *toOccur = 
00301                 (PendingInterrupt *)pending->SortedRemove(&when);
00302 
00303     if (toOccur == NULL)                // no pending interrupts
00304         return FALSE;                   
00305 
00306     if (advanceClock && when > stats->totalTicks) {     // advance the clock
00307         stats->idleTicks += (when - stats->totalTicks);
00308         stats->totalTicks = when;
00309     } else if (when > stats->totalTicks) {      // not time yet, put it back
00310         pending->SortedInsert(toOccur, when);
00311         return FALSE;
00312     }
00313 
00314 // Check if there is nothing more to do, and if so, quit
00315     if ((status == IdleMode) && (toOccur->type == TimerInt) 
00316                                 && pending->IsEmpty()) {
00317          pending->SortedInsert(toOccur, when);
00318          return FALSE;
00319     }
00320 
00321     DEBUG('i', "Invoking interrupt handler for the %s at time %d\n", 
00322                         intTypeNames[toOccur->type], toOccur->when);
00323 #ifdef USER_PROGRAM
00324     if (machine != NULL)
00325         machine->DelayedLoad(0, 0);
00326 #endif
00327     inHandler = TRUE;
00328     status = SystemMode;                        // whatever we were doing,
00329                                                 // we are now going to be
00330                                                 // running in the kernel
00331     (*(toOccur->handler))(toOccur->arg);        // call the interrupt handler
00332     status = old;                               // restore the machine status
00333     inHandler = FALSE;
00334     delete toOccur;
00335     return TRUE;
00336 }
00337 
00338 //----------------------------------------------------------------------
00339 // PrintPending
00340 //      Print information about an interrupt that is scheduled to occur.
00341 //      When, where, why, etc.
00342 //----------------------------------------------------------------------
00343 
00344 static void
00345 PrintPending(int arg)
00346 {
00347     PendingInterrupt *pend = (PendingInterrupt *)arg;
00348 
00349     printf("Interrupt handler %s, scheduled at %d\n", 
00350         intTypeNames[pend->type], pend->when);
00351 }
00352 
00353 //----------------------------------------------------------------------
00354 // DumpState
00355 //      Print the complete interrupt state - the status, and all interrupts
00356 //      that are scheduled to occur in the future.
00357 //----------------------------------------------------------------------
00358 
00359 void
00360 Interrupt::DumpState()
00361 {
00362     printf("Time: %d, interrupts %s\n", stats->totalTicks, 
00363                                         intLevelNames[level]);
00364     printf("Pending interrupts:\n");
00365     fflush(stdout);
00366     pending->Mapcar(PrintPending);
00367     printf("End of pending interrupts\n");
00368     fflush(stdout);
00369 }

Generated on Mon Feb 10 09:54:45 2003 for nachos by doxygen1.2.14 written by Dimitri van Heesch, © 1997-2002
The University of Southern California does not screen or control the content on this website and thus does not guarantee the accuracy, integrity, or quality of such content. All content on this website is provided by and is the sole responsibility of the person from which such content originated, and such content does not necessarily reflect the opinions of the University administration or the Board of Trustees