Introduction
The goal of this lab assignment is to extend the LC-3b simulator you wrote in Lab 2 to handle interrupts and exceptions. You will augment the existing LC-3b microarchitecture to support detection and handling timer interrupts and protection exceptions.
The input to this new simulator will be:
New shell code A modified shell code has been written for you:
lc3bsim3.c
You will need to copy and paste the code you wrote for Lab 2 into this new shell code.
To run the simulator, type:
lc3bsim3 <micro_code_file> <program_file_1> <program_file_2> ...
The first parameter is the microcode file as before. The second parameter is the user program you will write.
Specifications
This Lab consists of 2 parts, which we will describe in detail:
1. Adding support for timer interrupts
2. Adding support for protection exceptions
Timer Interrupt
In this lab, 1 timer interrupt will occur - at cycle 300. When the timer generates an interrupt, the microarchitecture may be in the middle of executing an instruction. You need to decide exactly when the interrupt is detected and add the necessary microarchitectural support to handle interrupts. When the interrupt is detected the following actions will be taken by the processor:
1. The privilege mode (most significant bit of the PSR, program status register) is set to 0, which indicates supervisor-level privilege. The interrupt service routine will be executed with supervisor-level privilege.
2. R6 is set to the supervisor stack pointer if that is not already the case.
3. The old PSR (PSR before PSR[15] is set to 0) and PC are pushed onto the supervisor stack. Supervisor stack pointer is decremented on each push. Note that the supervisor stack is different from user stack. Interrupt service routines can access the supervisor stack using R6. You should initialize the supervisor stack pointer to address 0x3000. Note that R6 will refer to the user stack while your program is running. It will refer to the supervisor stack while the interrupt service routine is being executed. If the system is in user mode when an interrupt is detected, the microarchitecture should transparently switch R6 so that it points to the supervisor stack. You need to implement this "stack switching" in microcode. You will need to modify the datapath, add new states to the state machine, and possibly add new control signals to support this operation. You may add registers to the datapath to save and restore user and supervisor stack pointers.
4. The interrupting event supplies its 8-bit interrupt vector (INTV). The interrupt vector for the timer interrupt is 0x01.
5. The processor left-shifts the interrupt vector one bit, yielding x02, and adds it to the base address of the interrupt/exception vector table (x200), yielding the address of the memory location (x0202), which contains the starting address of the interrupt service routine.
6. The contents of memory location x0202, which should be x1200 for this assignment, are read and loaded into the PC.
7. The processor begins execution of the interrupt service routine.
The first step in adding support for interrupts is to determine how the state diagram of the LC-3b can be modified to handle interrupts. You will have to augment the microsequencer with additional logic to sequence these new states, and extend the existing microinstructions with additional bits for both the microsequencer and the datapath. You may augment current microinstruction fields and add new fields. You may also add new logic to the datapath. You are free to implement this as you wish, but you must document your method.
RTI Instruction
Next, you have to implement an instruction for returning from an interrupt. This will be used in the interrupt service routine to transfer control back to the interrupted program. This instruction, called RTI (return from interrupt), has the opcode 1000, and pops the old PC and PSR off the supervisor stack. If the RTI transfers control back to a user-level program, then the microcode should switch the stack pointer back so that R6 points to the user stack. (See the ISA reference for details on the RTI instruction)
Protection Exception
Protection exceptions occur only when the machine is in user mode, and a memory location in system space (locations x0000 - x2FFF) is accessed by the user program. Exception handling for protection exceptions is very similar to interrupt handling as described above with an important difference: the exception-causing instruction should not be allowed to complete before the exception is handled. Hence, the memory access that causes the protection exception also should not be allowed to complete. You will need to change the mode of the machine to supervisor, switch to the supervisor stack, push the decremented PC and PSR on the supervisor stack and load the PC with the address of the exception service routine. The exception vector for protection exception is x02. You can store the exception vector in a separate EXCV register and add this register left-shifted by one to the interrupt/exception vector table base register to get the address of the location that contains the starting address of the exception service routine. You are free to implement this as you wish, but keep in mind the possibility of combining the states used for initiating the interrupt service routine and those used for initiating the exception service routine.
You will also write the exception service routine for the
protection exception. This routine should start at memory location
x1600. For the purposes of this assignment, the exception service
routine will simply halt the machine. However, don't rely on this. We
can test your simulator by replacing your exception routine with our
routine which returns from the exception handler using the RTI
instruction instead of halting the machine. Upon return from the
exception handler, the instruction that caused the exception should be
re-executed.
Tips on getting started
When designing the mechanisms to support interrupts and exceptions keep in mind that they are handled very similarly. In your microcode, states for pushing the PSR and PC on stack and loading the address of handler routines could be shared for both exception and interrupt handling.
Writing Code
The user program loaded into x3000 should do the following: initialize
memory location x4000 to #1, calculate the sum of the first 20 bytes
stored in the memory locations beginning with xC000. This sum should
first be stored at xC014. Also, store the sum at x0000. This store
will cause a protection exception. The following numbers should be
stored at locations xC000:
x12, x11, x39, x23, x02,
xF6, x12, x23, x56, x89, xBC, xEF, x00, x01, x02,
x03, x04, x05, x06, x07.
The interrupt service routine must increment memory location x4000.
The exception handlers you will submit should simply halt the machine.
What To Submit Electronically
How to generate the dumpsim file
Dump memory location x4000 and the registers once before the 300th cycle, once after the ISR is done, and finally after the protection exception halts the execution of the program.
Things To Consider
The user stack should start at address xFE00 and the supervisor stack should start at address x3000.