nnsched.txt 7.93 KB
==============================================================================

		NINTENDO64 TECHNICAL SUPPORT CENTER 
		
		    NINTENDO64 SAMPLE PROGRAM 1

		Copyright (C) 1997, NINTENDO Co., Ltd.

==============================================================================


The NTSC scheduler was specially developed for 'fast3D and 'fast3D_fifo' microcode.  The NTSC scheduler comprises a main thread, an audio thread and a graphics thread.  The functions of each of these three threads is described below.

* Main thread
	The scheduler task gets a retrace signal and NMI signal from the system and sends it to the client.

* Audio thread
	The audio task manages execution of a task to sound one frame's worth of audio data created by the audio manager upon receipt of a retrace signal.  The priority of the audio task is set higher than the priority of the graphic task, so it is executed in every frame.  If a graphic task is being executed on the RSP at the time that an audio task is started, the audio thread halts the graphic task with the osTaskYield function and restarts it after the audio task is done.

* Graphics thread
	Manages execution of the graphics task.  The graphics task gets the RSP display list and takes charge of the draw to the frame buffer.


In order to realize these above functions, the order of priority of the threads in set as follows:

			scheduler --> audio --> graphics

The thread priority is defined in nnSched.h as follows:

	#define NN_SC_PRI          120  /* scheduler */
	#define NN_SC_AUDIO_PRI    110  /* audio */
	#define NN_SC_GRAPHICS_PRI 100  /* graphics */


--------------------------------------------------------------------------------
nnScEventHandler()  -  Main thread (managing system events)


The main thread receives system event in block mode (line 82) and broadcasts system event to every registered client.  Two types of events are handled here: a retrace event (line 85) and an NMI event (line 88).


75 void
  76 nnScEventHandler(NNSched *sc)
  77 {
  78   OSMesg msg = (OSMesg)0;
  79 
  80   while(1) {
  81     /* receive event  */
  82     osRecvMesg(&sc->retraceMQ, &msg, OS_MESG_BLOCK);
  83 
  84     switch ( (int)msg ) {
  85     case VIDEO_MSG:		/* retrace signal process  */
  86       nnScEventBroadcast( sc, &sc->retraceMsg );
  87       break;
  88     case PRE_NMI_MSG:		/*  NMI signal process */
  89       nnScEventBroadcast( sc, &sc->prenmiMsg );
  90       break;
  91     }
  92   }
  93 }

--------------------------------------------------------------------------------
nnScExecuteAudio()  -   Executing an audio task


In line 164, the audio thread is waiting for a  request for execution of an audio task from the application.  When an execution request comes, the first action (line 166) is to flush all cache lines and update the contents of memory.  The next step is to verify that a graphics task is not occupying the RSP.  If a graphics task is being executed, then line 174 tells the RSP task to yield, and the system waits for an RSP task done message.  The received message can have one of two meanings.  Line 178 checks whether the graphics task has actually yielded: TRUE means the graphics has temporarily halted, FALSE means the graphic task process was completed before the task yield process conducted by line 174.  Either way, at this point the RSP can now be used, and line 183 executes the audio task and line 184 waits for end.   As the final process, if a graphics task is yielding, it is restarted in line 188.  If  the graphics task was already done when the yield process was conducted (yieldFlag = 2), then the RSP task done message received in line 175 is  returned to line 192.  Once all processes are completed, line 194 informs the application that the audio task is done and then the thread waits for the next request.




 153 void
 154 nnScExecuteAudio(NNSched *sc)
 155 {
 156   OSMesg msg = (OSMesg)0;
 157   NNScTask *task = (NNScTask *)0;
 158   NNScTask *gfxTask = (NNScTask *)0;
 159   u32 yieldFlag = 0;
 160 
 161   while(1) {
 162 
 163     /* wait for request to execute audio task */
 164     osRecvMesg(&sc->audioRequestMQ, (OSMesg *)&task, OS_MESG_BLOCK);
 165 
 166     osWritebackDCacheAll();	/* flush cache */
 167   
 168     /* check current RSP status */
 169     yieldFlag = 0;
 170     gfxTask = sc->curGraphicsTask;
 171     if( gfxTask ) {
 172 
 173       /* wait for end (yield) of graphics task */
 174       osSpTaskYield();		/* yield task */
 175       osRecvMesg(&sc->rspMQ, &msg, OS_MESG_BLOCK);
 176 
 177       /* check is task actually yielding */
 178       if (osSpTaskYielded(&gfxTask->list))
 179 	        yieldFlag =1;
 180 	   else yieldFlag =2;
 181     }
 182     /* wait for end of RSP task */
 183     osSpTaskStart(&task->list);        /* execute task */
 184     osRecvMesg(&sc->rspMQ, &msg, OS_MESG_BLOCK);
 185 
 186     /* restart yielding graphics task */
 187     if( yieldFlag == 1) {
 188       osSpTaskStart(&gfxTask->list);    
 189     }
 190     else if( yieldFlag == 2 )
 191       osSendMesg(&sc->rspMQ, &msg, OS_MESG_BLOCK);
 192 
 193     /* inform thread which started audio task that task is done */
 194     osSendMesg(task->msgQ, task->msg, OS_MESG_BLOCK);
 195   }
 196 }

--------------------------------------------------------------------------------
nnScExecuteGraphics() --  Executing a graphics task


In line 210, the graphics thread is waiting for a  request for execution of a graphics task from the application.  When an execution request comes, line 213 performs wait until frame buffer can be used.  Once frame buffer becomes available, the graphics task is executed.  Line 219 receives RSP task done message, and line 223 receives RDP process done message.


 201 void
 202 nnScExecuteGraphics(NNSched *sc)
 203 {
 204   OSMesg msg = (OSMesg)0;
 205   NNScTask *task;
 206 
 207   while(1) {
 208 
 209     /* wait for request to execute graphics task */
 210     osRecvMesg(&sc->graphicsRequestMQ, (OSMesg *)&task, OS_MESG_BLOCK);
 211 
 212     /* wait for frame buffer to become available for use */
 213     nnScWaitTaskReady(sc, task);
 214 
 215     osSpTaskStart(&task->list);        /*  execute task */
 216     sc->curGraphicsTask = task;
 217 
 218     /*  wait for end of RSP task */
 219     osRecvMesg(&sc->rspMQ, &msg, OS_MESG_BLOCK);
 220     sc->curGraphicsTask = (NNScTask *)0;
 221 
 222     /* wait for end of RDP task */
 223     osRecvMesg(&sc->rdpMQ, &msg, OS_MESG_BLOCK);
 224 
 225     /* invalidate video blackout first time only */
 226     if (sc->firstTime) {
 227       osViBlack(FALSE);
 228       sc->firstTime = 0;
 229     }
 230 
 231     /* specify next frame buffer to display */
 232     if ( task->flags & NN_SC_SWAPBUFFER )
 233       osViSwapBuffer(task->framebuffer);
 234 
 235     /* inform thread which started graphics task that task is done */
 236     osSendMesg(task->msgQ, task->msg, OS_MESG_BLOCK);
 237   }
 238 }


--------------------------------------------------------------------------------
nnScWaitTaskReady() -  Wait for frame buffer to become available for use


This function in line 251 checks whether frame buffer is being used (either displaying current frame or set to display next frame) and, if so, waits in block mode for next update. In order to perform this process, line 252 prepares to receive a retrace signal as its own client by registering a wait message queue in the scheduler.  In line 253 the next retrace signal can be received, so in line 254 it deletes itself from the clients.  Here an idle loop is used so other threads cannot execute.



 243 void
 244 nnScWaitTaskReady(NNSched *sc, NNScTask *task)
 245 {
 246   OSMesg msg = (OSMesg)0;
 247   NNScClient client;
 248   void *fb = task->framebuffer;
 249 
 250   /* If frame buffer is not empty wait until next retrace */
 251   while( osViGetCurrentFramebuffer() == fb || osViGetNextFramebuffer() == fb ) {
 252     nnScAddClient( sc, &client, &sc->waitMQ );  
 253     osRecvMesg( &sc->waitMQ, &msg, OS_MESG_BLOCK );
 254     nnScRemoveClient( sc, &client );
 255   }
 256 }