Devices
- have a control satus reguister and a data register
- 0: CSR [x][x][x][ena][OF][DBA][IO][IE]
^ ^ ^ ^ ^
c s s c c
c = control s = status
Exceptions
- an evenet that causes an application to stop what its doing
- ecternal to CPU: interrupts
- internal to CPU: faults
- application dependent: traps
- invonultary and voluntary relinquishing of CPU
Handling exceptions
- applications have no control over interrupt or fault exceptions
→ an application can disable interrupts
- applications explicitly issue trap exceptions
- exception handler is responsible for determining the cause of the exception and processing it
- control might (or might not) return to the application
- in a fault, the OS will have to say what is happening
- in a trap, the application makes an explicit call to a piece of sofrare, probably in the OS
- faults are also probably handled by the OS if not the kernel
- An exception call is different from a subroutine call
- Where is the exception handler?
- in a subroutine call we save the state, e.g. return address in the link register
- in an exception
- we need to know where we're executing in the application
→ we save the PC, to return to it
→ save the PSW
→ link register (in our case)
→ may have to save other stuff but this is ok for now
→ don't save the entire register file because that would be too expensive
→ stack pointer also has to be saved
→ CEX state also needs to be saved
- each exception handler has an entry point stored in the interrupt vector table
- cpu changes pc ← vectortable[exception]
- exception occurs:
→ cpu pushes PC
→ cpu pushes PSW
→ PSW . IntEna ← disbaled
→ pc ← vectorTable[exception number]
- control passes to exception handler
→ other registers can be saved
→ exception specific code performed
→ saved registers restored
- exit involves a special isntruction
→ IRET, RTI, etc
→ pulls PSW
→ pulls PC
- all interrupts are blocked for the duration of the ISR
- pending interrupts are delayed until application resumes and executes an instruction
- pending interrupts can be lost
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Priority
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
XM23
- PSW holds current priority of applicaiton
- interrupts:
→ devices: clock :5 kb :4 screen: 4
→ allows nested interrupts
→ interrupt priority > current priority
- cpu starts at priority 7
all applications have a priority, all devices and applications
a higher priority device can in terrupt an application with a lower prioprity
an application cannot trap to an ewuial or lwoer priority, only to a higher priority
an application cannot raise its priority to an equal or higher priority, only to a lower priority
trap can go low→high
setpri can go high→low
Interrupt vector
- need: pew PSW with higher priority, exception handler's entry point
- [prev][prev][prev][x][x][x][x][FLT][Current][current][current][V][SLP][N][Z][C] ← New PSW
- [ ~~~~~~~~ Handler entry point address ~~~~~~~~ ]
- prev stores the priority of the thing running before the interrupt occurred, so control can be passed to the correct level when the exception is finished handling
2023/07/18 - 12:58
continued:
in an interrupt vector:
- previous should be 0
- current is the new cpu priority
-vector 15 is the entry point for system restart
Handling exceptions in xm23
- on enrty, save PC, LR, PSW, CEX state
- XM23 and aRM do not have and explicit exception rteturn system
fetch()
- inst ← mem[pc]
- if pc = #FFFF then
→ pull CEX
→ pull PSW
→ pull LR
→ pull PC
- then pc should have the right value, do:
- inst ← mem[pc]
when the exception took place we pushed all of that onto the stack, now that its finished this is the routine to pull it.
if pc is odd then:
→ illegal address exception
- push all the registers (PC , PSW, LR, CEX)
- new PC from the vector table
- LR ← FFFF
- then do a fetch
- FFFF means youve finished an exception handler and you have to pull everything it pushed.
2023/07/20 - 12:10
idrk what heading this falls under
for i = 0 to cachesize do
if CL[i].addr = MAR then
HIT
- break; ← the problem is we don't know why we're breaking
-
-
endif
endfor
other way:
i = 0
while( i < cachesize && CL[].addr != MAR
i++
- writing separate functions for each instructions is tedious because a lot of them use the same arguments
What are the exceptions we can have when the CPU is running?
- faults
→ invalid/illegal instruction
⇒ IR holds invalid opcode
→ invalid address
⇒ PC is odd
⇒ memory access word + odd address
→ priority fault
→ double fault
- supervisory call
- switch on the IR.. default case: fault
- fetch()... PC is odd! we have to change the flow of control.
- we also have the exception handler, which we write ourselves (not a part of the cPU)
- when a fault occurs we want to pass control to the exception handler. How do we do that?
- when the fault occurs, the cpu knows the fault occurs. how does it know? because in our emulator we did a switch that idicated a fault or when we did our fetch we indicated a fault. The cpu also can know which fault has occured if we give each fault an ID.
- We will have a simple table with fault IDs that will link them do the addresses of their exception handler. If it knows the fault # it can look up the handler from the table.
- The fualt tbale is in a known location in high memory so the person writing the operaitng system can write their own exception handlers and put the addresses in that table.
- we want to save our state information to let the person debugging the system know where we went wrong.
→ push the PC
→ push the PSW
→ push LR
→ push CEX
- The exception handler has to be of higher priority than the application
- In thhe table youll have the exception handlers PSW and its entry point. in its PSW will be its priority.
- when the fault occurs and we push all the stuff, we have that and we also have the fault number.
- new psw ← interruptvector[faultnumner] psw
- psw.cp < new psw.cp ← if this happens something is wrong because the new priority needs to be higher
- psw ← new psw
- PC ← interrupt vector[flt#] . entry point
- psw.flt ← true
What about whenwe want to do a return?
We can't just go back to LR.
LR will be change to a value that signals that an exception has occurred.
LR ← 0xFFFF
in EH:
MOVL LR, PC
PC is now 0xFFFF
The slight trick you can use is as soon as you enter the exception handler you can do a MOV LR, -SP. (put the new LR on the stack)
now when the emulator tries to do a fetch, it detects an invalid address
if PC == 0xFFFF then we are returning from a fault:
pull CEX, PSW, LR, PC
now the PC has the correct value and it's business as usual.
Traps
with a trap, or a SVC(supervisory call) we specify whatever entry point/vector we want SVC #. If we treat that as the fault number, what can we do.
The logic is exactly the same... push all state informatioon... get the psw from the vector table (but its not a fault)
it gets whatever it wants from the vector table and hte LR gets 0xFFFF and then it goes through the return process...
If it was a true exception (SVC isnt an exception) you probably wouldn't return you would just abort.
SVC can call fault handlers, device handlers... but they are not faults
TRAPS are SVCs.
It passes a fault number to the vector table and calls the fault handler but it is not an actual fault. It's kinda like using the handler as a subroutine.
INV_ADDR ← 8
INV_INSTR ← 9
Vlads question about a double fault:
assume for the sake of argument that we have an application that runs along and has a fault
controll passes to exception handler 1
it starts executing and it has a fault
we can't have multiple faults, becuase that means something is very very wrong.
what we do instead is we have the psw.FLT gets TRUE
when a flt occurs, we have fault code:
we check if PSW.flt is set. If it is set when a flt occurs, we have a double fault.
a double fault would probably be priority 7
To distinguish between traps and faults,
if we havea fault we will call fault handler with two arguments. fault number or SVC number, but the second argument indicates whether it's a fault or not.
in the fault handler
if psw.fault and fault argument == true.... double fault!
when we use a trap we call the fualt handler with the fault number but we put “not fault” as the second argument
2023/07/25 - 12:10
I think hes recapping
Interrupt: is legal within CEX
When an interrupt happens:
push
- PC
- LR PSW
- CEX state
PSW gets vector[drevice1].PSW
PSW.slp ← 0
PC ← vector[device1].PC
LR ← #FFFF
CEX state is cleared
The most expensive way to check for interrupts is to have an interrupt line fo every device. this is a bad idea because it limits the number of thedevices and wastes real estate if theyre not all used at o9nce
The other approach is a single interrupt line connected to an OR gate. Then if any device has an interrupt it will be detected, but you dont know which one caused it.
You would have a polling table for the ISR. it is sorted in order of priority e.g. disc and keyboard would be am,oing the highest priority.
interrupt controller gets a singnal from the device - it had a status change. it compes into the interrupt controller. it knows which device has been interrupting, it also knows the device number. it uses the device number as the interrupt vector number. CPUs interrupt vectors are only a single word.
If another device interrupts and another one is taking place, that one is pending until an acknowledgment comes back from the CPU to say the last interrupt has finished. After the ack, the interrupt controller can look at whos waiting next.
Interrupt storm: CPU is doing nothing but servicing interrupts from the same device over and over
Increasing throughput
- single register
→ fill the data register Index