Ejemplo n.º 1
0
/**
	Simulate the Memory Management Unit (MMU) that translates the contents of
	the Memory Address Register (MAR) to a real physical address.

	Use the contents of MAR = [segment,offset] as the logical address to be
	translated to a physical address.

	<b> Algorithm: </b>
	\verbatim
	Set segment to the segment saved in the MAR:
		If in kernel mode (CPU's mode equals 1)
			Set segment to be in upper half of Mem_Map (MAR.segment+Max_Segments)
		Otherwise, in user mode (CPU's mode equals 0)
			Just use lower half of Mem_Map (MAR.segment)

	If illegal memory access (access == 0)
		Create seg. fault event at the current time using the CPU's active process's terminal position (+ 1) as the agent ID
		Return -1

	If address is out of the bounds
		Create address fault event at the current time using the CPU's active process's terminal position (+ 1) as the agent ID
		Return -1

	Compute physical memory address:
		Address = base address from segment in Mem_Map + offset saved in MAR

	Return address
	\endverbatim

	@retval address -- physical address into Mem obtained from logical address
		stored in MAR
 */
int
Memory_Unit( )
{
	//Set segment to the segment saved in the MAR:
	int currSeg, currAddress;

	//If in kernel mode (CPU's mode equals 1)
	if(CPU.state.mode == 1)
	{
		//Set segment to be in upper half of Mem_Map (MAR.segment+Max_Segments)
		currSeg = MAR.segment + Max_Segments;
	}
	
	//Otherwise, in user mode (CPU's mode equals 0)
	else
	{
		//Just use lower half of Mem_Map (MAR.segment)
		currSeg = MAR.segment;
	}
	
	//If illegal memory access (access == 0)
	if(Mem_Map[currSeg].access == 0)
	{
		//Create seg. fault event at the current time using the CPU's active process's terminal position (+ 1) as the agent ID
		Add_Event(SEGFAULT_EVT, CPU.active_pcb->term_pos + 1, &Clock);
		
		//Return -1
		return -1;
	}

	//If address is out of the bounds
	if(currSeg > (Max_Segments*2 - 1))
	{
		//Create address fault event at the current time using the CPU's active process's terminal position (+ 1) as the agent ID
		Add_Event(ADRFAULT_EVT, CPU.active_pcb->term_pos + 1, &Clock);
		//Return -1
		return -1;
	}
	
	//Compute physical memory address:
	
	//Address = base address from segment in Mem_Map + offset saved in MAR
	currAddress = Mem_Map[currSeg].base + MAR.offset;
	
	//Return address
	return currAddress;
}
Ejemplo n.º 2
0
/**
	Start processing I/O request for given device.

	<b> Important Notes:</b>
	\li Add_time() and Diff_time() should be used for calculating and
	incrementing many of the simulation time fields, such as the device's busy
	time.
	\li The time at which I/O completes is obtained using rb->bytes and the
	speed of the device: device->bytes_per_sec and device->nano_per_byte.
	Using all these values, the seconds and nanoseconds fields of a simulation
	time structure can be filled in. Note that bytes per sec and nano per byte
	are, to some degree, reciprocals, which implies that one should divide by
	the former but multiply with the latter.
	\li When finally adding the ending I/O event to the event list, the
	device's ID must be converted into an agent ID. Recall in objective 1 that
	agent IDs for devices were set to directly follow terminal user IDs.

	<b> Algorithm: </b>
	\verbatim
	If the device is busy
		Do nothing

	If the device has no requests to service in its waiting queue
		Do nothing

	Get and remove first node from waiting list

	Mark rb as the current request block in the device; set rb's status as active

	Update time spent waiting in queue--calculate difference between current time and time it was enqueued and add this value to the current queue wait time--use Add_time() and Diff_time()

	Compute length of time that I/O will take (transfer time)--compute number of seconds and then use the remainder to calculate the number of nanoseconds

	Increment device's busy time by the transfer time--use Add_time()

	Compute time I/O ends--ending time is the current time + transfer time--use Add_time() and Diff_time()

	Update the number of IO requests served by the device

	Add ending I/O device interrupt to event list
	\endverbatim

	@param[in] dev_id -- ID of device
	@retval None
 */
void
Start_IO( int dev_id )
{

    //If the device is busy
    if(Dev_Table[dev_id].current_rb != NULL && Dev_Table[dev_id].current_rb->status == ACTIVE_RB)
    {
        //Do nothing
        return;
    }

    //If the device has no requests to service in its waiting queue
    if(Dev_Table[dev_id].wait_q == NULL)
    {
        //Do nothing
        return;
    }

    rb_list *rbNode = Dev_Table[dev_id].wait_q;

    //Get and remove first node from waiting list
    Dev_Table[dev_id].wait_q->prev->next = Dev_Table[dev_id].wait_q->next;
    Dev_Table[dev_id].wait_q->next->prev = Dev_Table[dev_id].wait_q->prev;
    Dev_Table[dev_id].wait_q = rbNode->next;

    if(rbNode == Dev_Table[dev_id].wait_q)
    {
        Dev_Table[dev_id].wait_q = NULL;
    }

    rbNode->next = rbNode;
    rbNode->prev = rbNode;

    //Mark rb as the current request block in the device; set rb's status as active
    Dev_Table[dev_id].current_rb = rbNode->rb;
    rbNode->rb->status = ACTIVE_RB;

    //Update time spent waiting in queue--calculate difference between current time and time it was enqueued and add this value to the current queue wait time--use Add_time() and Diff_time()
    time_type currTime = Clock;
    Diff_time(&rbNode->rb->q_time, &currTime);
    Add_time(&currTime, &Dev_Table[dev_id].total_q_time);

    //Compute length of time that I/O will take (transfer time)--compute number of seconds and then use the remainder to calculate the number of nanoseconds
    time_type transferTime;
    transferTime.seconds = rbNode->rb->bytes / Dev_Table[dev_id].bytes_per_sec;
    transferTime.nanosec = (rbNode->rb->bytes % Dev_Table[dev_id].bytes_per_sec)*Dev_Table[dev_id].nano_per_byte;

    //Increment device's busy time by the transfer time--use Add_time()
    Add_time(&transferTime, &Dev_Table[dev_id].total_busy_time);

    //Compute time I/O ends--ending time is the current time + transfer time--use Add_time() and Diff_time()
    time_type ioEndTime = transferTime;
    Add_time(&Clock, &ioEndTime);

    //Update the number of IO requests served by the device
    Dev_Table[dev_id].num_served++;

    //Add ending I/O device interrupt to event list
    Add_Event(EIO_EVT,(dev_id+Num_Terminals+1),&ioEndTime);

}
Ejemplo n.º 3
0
/**
	Start processing I/O request for given device.

	<b> Important Notes:</b>
	\li Add_time() and Diff_time() should be used for calculating and
	incrementing many of the simulation time fields, such as the device's busy
	time. 
	\li The time at which I/O completes is obtained using rb->bytes and the
	speed of the device: device->bytes_per_sec and device->nano_per_byte.
	Using all these values, the seconds and nanoseconds fields of a simulation
	time structure can be filled in. Note that bytes per sec and nano per byte
	are, to some degree, reciprocals, which implies that one should divide by
	the former but multiply with the latter.
	\li When finally adding the ending I/O event to the event list, the
	device's ID must be converted into an agent ID. Recall in objective 1 that
	agent IDs for devices were set to directly follow terminal user IDs.

	<b> Algorithm: </b>
	\verbatim
	If the device is busy
		Do nothing

	If the device has no requests to service in its waiting queue
		Do nothing

	Get and remove first node from waiting list

	Mark rb as the current request block in the device; set rb's status as active

	Update time spent waiting in queue--calculate difference between current time and time it was enqueued and add this value to the current queue wait time--use Add_time() and Diff_time()

	Compute length of time that I/O will take (transfer time)--compute number of seconds and then use the remainder to calculate the number of nanoseconds

	Increment device's busy time by the transfer time--use Add_time()

	Compute time I/O ends--ending time is the current time + transfer time--use Add_time() and Diff_time()

	Update the number of IO requests served by the device

	Add ending I/O device interrupt to event list
	\endverbatim

	@param[in] dev_id -- ID of device
	@retval None
 */
void
Start_IO( int dev_id )
{

	// DECLARE VARIABLES
	struct rb_list* rbListNode;
	struct rb_type* rb;
	struct time_type wait_time;
	struct time_type transfer_time;
	struct time_type eio_time;

	// TODO: verify discrepancy of: if(){ return } conditions

	// TODO: change to "==" (?)
	//If the device is busy
	if( Dev_Table[ dev_id ].current_rb != NULL ){
		//Do nothing
		return;
	}

	//If the device has no requests to service in its waiting queue
	if( Dev_Table[ dev_id ].wait_q == NULL ){
		//Do nothing
		return;
	}

	// TODO: revisit discrepancy
	//Get and remove first node from waiting list
	rbListNode = Dev_Table[ dev_id ].wait_q;

	// also store the node's request block to rb since we're freeing the node
	rb = rbListNode->rb;

	// does wait_q contain >1 node?
	if( rbListNode->next != rbListNode ){
		// wait_q contains more than 1 node
		rbListNode->prev->next = rbListNode->next;
		rbListNode->next->prev = rbListNode->prev;
		Dev_Table[ dev_id ].wait_q = Dev_Table[ dev_id ].wait_q->next;
	} else {
		// wait_q contains exactly 1 node
		rbListNode->next = NULL;
		rbListNode->prev = NULL;
		Dev_Table[ dev_id ].wait_q = NULL;
	}

	// TODO: free rbListNode (?)

	//Mark rb as the current request block in the device; set rb's status as active
	Dev_Table[ dev_id ].current_rb = rb;
	rb->status = ACTIVE_RB;

	// TODO: revisit discrepancy on all time calculations below

	//Update time spent waiting in queue--calculate difference between current time and time it was enqueued and add this value to the current queue wait time--use Add_time() and Diff_time()
	wait_time = Clock;
	Diff_time( &( rb->q_time ), &wait_time );
	Add_time( &wait_time, &( Dev_Table[ dev_id ].total_q_time ) );

	//Compute length of time that I/O will take (transfer time)--compute number of seconds and then use the remainder to calculate the number of nanoseconds
	transfer_time.seconds =   rb->bytes / Dev_Table[ dev_id ].bytes_per_sec;
	transfer_time.nanosec = ( rb->bytes % Dev_Table[ dev_id ].bytes_per_sec )
	 * Dev_Table[ dev_id ].nano_per_byte;

	//Increment device's busy time by the transfer time--use Add_time()
	Add_time( &transfer_time, &( Dev_Table[ dev_id ].total_busy_time ) );

	//Compute time I/O ends--ending time is the current time + transfer time--use Add_time() and Diff_time()
	eio_time = Clock;
	Add_time( &transfer_time, &eio_time );

	//Update the number of IO requests served by the device
	Dev_Table[ dev_id ].num_served = Dev_Table[ dev_id ].num_served + 1;

	//Add ending I/O device interrupt to event list
	Add_Event( EIO_EVT, dev_id + Num_Terminals + 1, &eio_time );

}
Ejemplo n.º 4
0
/**
	Simulate the Central Processing Unit.

	The CPU, in general, does the following:
	\li Fetches the instruction from memory (Mem)
	\li Interprets the instruction.
	\li Creates a future event based on the type of instruction and
	inserts the event into the event queue.

	Set_MAR() is called to define the next memory location for a Fetch().
	The first fetch from memory is based on CPU.state.pc. Instructions that
	create events are:
	\li WIO   							
	\li SIO							
	\li END							
	The SKIP and JUMP instructions are executed in "real-time" until
	one of the other instructions causes a new event.

	An addressing fault could be generated by a call to Fetch() or Write(), so
	Cpu() should just return in this case (the simulator will later call
	Abend_Service() from the interrupt handler routine to end the simulated
	program).

	<b> Important Notes:</b>
	\li Use a special agent ID, i.e., 0, to identify the boot program. For
	all other objectives, the agent ID should be retrieved from the CPU's
	currently running process--use this process's terminal position and add 1
	so that it does not conflict with boot.
	\li The only instructions processed by Cpu() are SIO, WIO, END, SKIP, and
	JUMP. No other instructions, such as REQ and device instructions, are
	encountered in Cpu(). This is by incrementing the program counter by 2 to
	bypass these unused instructions.
	\li A while(1) loop is needed to correctly evaluate SKIP and JUMP
	instructions since multiple SKIP and JUMP instructions may be encountered
	in a row. The loop is exited when an SIO, WIO, or END instruction is
	encountered.
	\li The function Burst_time() should be used to convert CPU cycles for SIO,
	WIO, and END instructions to simulation time (time_type).

	<b> Algorithm: </b>
	\verbatim
	Identify the agent ID for the currently running program:
		If CPU has no active PCB, then the program is boot
		Otherwise, the program is the active process running in the CPU, so use its ID

	Loop forever doing the following:
		Set MAR to CPU's program counter
		Fetch instruction at this address
		If fetch returns a negative value, a fault has occurred, so
			Return

		Determine type of instruction to execute
			If SIO, WIO, or END instruction
				If the objective is 3 or higher
					Increment total number of burst cycles for PCB
				Calculate when I/O event will occur using current time + burst time
				Add event to event list
				Increment PC by 2 to skip the next instruction--device instruction
				Return from Cpu() (exit from loop)

			If SKIP instruction
				If count > 0,
					Decrement count by 1
					Write this change to memory by calling Write()
					If write returns a negative value, a fault has occurred, so
						Return
					Increment the CPU's PC by 2 since the next instruction is to be skipped
				Otherwise, instruction count equals 0, so
					Increment the CPU's PC by 1 since the next instruction, JUMP, is to be executed
				Continue looping

			If JUMP instruction
				Set the PC for the CPU so that the program jumps to the address determined by the operand of instruction
				Continue looping
	\endverbatim 

	\retval None
 */
void
Cpu( )
{
	time_type *eventTime;
	int counter;
	instr_type *instruction;
	instruction = (instr_type *) malloc(sizeof(instr_type));
	eventTime = (time_type *) malloc(sizeof(time_type));
	//Identify the agent ID for the currently running program:

	//If CPU has no active PCB, then the program is boot
	if(CPU.active_pcb == NULL)
	{
		Agent = 0;
	}
	//Otherwise, the program is the active process running in the CPU, so use its ID
	//use this process's terminal position and add 1
	//so that it does not conflict with boot.
	else
	{
		Agent = CPU.active_pcb->term_pos + 1;
	}
	//Loop forever doing the following:
	while(1)
	//~ for(counter = 0; counter < 3; counter++)
	{
		
		//Set MAR to CPU's program counter
		Set_MAR(&CPU.state.pc);
		//Fetch instruction at this address
		int fetch = Fetch(instruction);
		//If fetch returns a negative value, a fault has occurred, so
		if(fetch < 0)
		{
			//Return
			return;
		}

		//Determine type of instruction to execute
		
		//If SIO, WIO, or END instruction
		if(instruction->opcode == 0 || instruction->opcode == 1 || instruction->opcode == 5)
		{
			//If the objective is 3 or higher
			if(Objective >= 3)
			{
				//Increment total number of burst cycles for PCB
				//~ CPU.active_pcb->sjnburst++;
				CPU.active_pcb->sjnburst += instruction->operand.burst;
			}
			
			//Calculate when I/O event will occur using current time = clock + burst time 
			Burst_time(instruction->operand.burst, eventTime);
			Add_time(&Clock, eventTime);

			//Add event to event list
			int eventID;
			switch(instruction->opcode)
			{
				case SIO_OP:
					eventID = SIO_EVT;
					break;
				case WIO_OP:
					eventID = WIO_EVT;
					break;
				case END_OP:
					eventID = END_EVT;
					break;
			}
			Add_Event(eventID, Agent, eventTime);
			//Increment PC by 2 to skip the next instruction--device instruction
			CPU.state.pc.offset += 2;
			//Return from Cpu() (exit from loop)
			return;
		}

		//If SKIP instruction
		else if(instruction->opcode == 4)
		{
			//If count > 0,
			if(instruction->operand.count > 0)
			{
				//Decrement count by 1
				instruction->operand.count--;
				
				//Write this change to memory by calling Write()
				int write = Write(instruction);
				//If write returns a negative value, a fault has occurred, so
				if(write < 0)
				{
					//Return
					return;
				}
				//Increment the CPU's PC by 2 since the next instruction is to be skipped
				CPU.state.pc.offset += 2;
			}
			
			//Otherwise, instruction count equals 0, so
			else
			{
				//Increment the CPU's PC by 1 since the next instruction, JUMP, is to be executed
				CPU.state.pc.offset++;
			}
			//Continue looping
		}

		//If JUMP instruction
		else if(instruction->opcode == 3)
		{
			//Set the PC for the CPU so that the program jumps to the address determined by the operand of instruction
			CPU.state.pc.segment = instruction->operand.address.segment;
			CPU.state.pc.offset = instruction->operand.address.offset;
			//Continue looping
		}
	}
}