Processor Exceptions

My first go at exceptions is working well. The basic idea is that moxie will have a single exception handling routine whose address lives in special register 1. You set the exception handler like so:

void install_handler(void (*handler)(void))
{
  printf ("Installing handler 0x%x\n", (unsigned) handler);
  asm("ssr %0, 1" : : "r" (handler));
}

When the processor hits an exception, it performs a standard function call to the handler. We return from the handler just like it was any old function, since it currently uses the standard C ABI. The exception type will be found in special register 2. The current exception types are as follows:

#define MOXIE_EX_DIV0 0 /* Divide by zero */
#define MOXIE_EX_BAD  1 /* Illegal instruction */
#define MOXIE_EX_IRQ  2 /* Interrupt request */
#define MOXIE_EX_SWI  3 /* Software interrupt */

In the case of IRQ and SWI exceptions, the interrupt number will be found in special register 3. I don’t have instructions yet to disable or enable interrupts, but those are an obvious next step. Here’s a sample exception handler:

void handler (void)
{
  int et;

  /* Get the exception handler from special register 2.  */
  asm("gsr %0, 2" : "=r"(et) : "0"(et) );

  switch (et)
    {
    case MOXIE_EX_DIV0:
      printf("DIVIDE BY ZERO EXCEPTION\n");
      break;
    case MOXIE_EX_BAD:
      printf("ILLEGAL INSTRUCTION EXCEPTION\n");
      break;
    case MOXIE_EX_IRQ:
      {
        int irq;
        asm("gsr %0, 3" : "=r"(irq) : "0"(irq) );
        printf("INTERRUPT REQUEST %d\n", irq);
      }
      break;
    case MOXIE_EX_SWI:
      {
        int swi;
        asm("gsr %0, 3" : "=r"(swi) : "0"(swi) );
        printf("SOFTWARE INTERRUPT REQUEST %d\n", swi);
      }
      break;
    default:
      printf("UNKNOWN EXCEPTION 0x%x\n", et);
      break;
    }
}

The handler for DIV0 and SWI may also want to know where the exception happened. This can be determined by pulling the return address off of the stack and subtracting the appropriate instruction length (2 for div and 6 for swi).

I’ve implemented support for this in the qemu port, and the test directory in moxiedev contains a simple program to exercise this new functionality. I think we’ll want to hook up some peripherals in qemu to the IRQ system soon.

Tags:

View Comments to “Processor Exceptions”

  1. neal says:

    >> I don’t have instructions yet to disable or enable interrupts

    once you have those, you will find “return from the handler just like it was any old function” is inadequate — you need an atomic instruction that (re-)enables exceptions and returns from exception. Without this atomic instruction, there is a race condition where a new exception could occur between re-enabling exceptions and returning from exception. Result: stack misery.

    You won’t detect that race until you’ve finished your Verilog coding and coded a very good test bench/test suite… probably as much if not more work than coding the processor.

    Typically, two exception enable bits are provided, and they behave as a tidy stack, so that your exception handlers can handle nested exceptions.

  2. neal says:

    D’oh.. typo:

    tidy stack –> tiny stack

  3. green says:

    Thanks for the tip! (I just noticed this old comment buried amongst all of the spam comments. Comment spam is a PITA.)

  4. [...] to invoke system calls. This means creating an exception handler and installing it as described in this old post. As an aside, the swi instruction takes a 32-bit immediate operand. We currently use this to [...]

Leave a Reply

blog comments powered by Disqus