Interrupt handling should be seriously considered because we'll need them one day or another. This means that the complete state of the core must be saved and restored by suitable hardware and software.
- A first issue is how to save the flags (C, S and Z). Attempting to save them by conditional instructions will destroy their values... Upon a IRQ signal, they would be saved to 3 backup bits which can be read and written by an IO port (port 0 ?). Exit from IRQ (and restoration of the IRQ mask) would occur when writing the value back to the port... or something like that.
Cost : 3 DFF with enable, 3 MUX for the feedback to the flags, 3 MUX to select where the DFF input comes from, and some glue logic.
- A second issue is to save the PC. Actually, it's PC+1 that must be saved (after LDC has completed). Again : the value can be saved to a IO port (port #1 ?) where it can be read and written (with the proper MUXes).
- A 3rd issue is that some scratch space is required to save a couple of registers (such as the address registers) to allow memory to be used to save the other registers. At least, ONE backup is required, probably A1, it can be automatic (like PC and Flags) but it's not required, the Interrupt Service Routine can start with OUT A1 2 for example (2 being the scratch register's address). If more scratch registers are provided (let's say 2 or 3) then very short ISR can be written, with no need to touch memory. However, memory is the main channel of communication between threads so a compromise is 2 scratch registers.
Overall, this means that the very first IO port addresses are reserved for core functions. There are 4 registers that can be written from the core's internal state, as provided by the entity's ports (PC+1, A1, A2 and flags are available outside of the datapath because they are required for the debug system). So far the map is :
- 00h : Flags (C, S, Z) and Interrupt control backup register. Values come from IO write port or core. Setting bit 0 triggers restoration of the previous states (sort of "return from IRQ"). Bit 1 would enable/disable the IRQ mask.
20180307: Two other bits control the mapping of memory banks for A1 and A2. These 8 bits are almost fully used.
- 01h : PC backup : value comes from IO port or core (this allows IRQ re-entrance)
- 02h : ScratchRegister1 : copied from A1 or from IO write port (result bus), used only by the ISR.
- 03h : ScratchRegister2 : copied from A2 or from IO write port (result bus), used only by the ISR.
Saving A1 and A2 directly with dedicated hardware saves one or two cycles of latency and some precious bytes) when servicing IRQs but can also make the core harder to route... So they might be simple registers (which saves a MUX as well as the required wires). Or they can be "shadow" registers, written everytime the corresponding A register is being written (but the value goes through the RESULT bus, while the OUT bus is connected to DST, so it's awkward and would increase the overall electrical activity of the circuit, which is less good for power draw).
One nice side-effect is : this avoids creating an opcode for RTI (ReTurn from Interrupt) because it is detected by the following conditions : OPCODE=OUT (5 bits), IMM8=0 (8 bits), and DST=1 (1 bit). 14 bits are easy to check in the pipeline.
The other nice aspect is that this mechanism is entirely optional : it can be disabled/removed if IRQs are not supported by the core.