Changes to PC/IMM/Except design

Posted on Sun 11 August 2024 in MUPS16

Following on from last month's musings, over the last couple of months I've done a bit more work on the LLVM backend for the MUPS/16. Whilst implementing support for global symbols (which includes function addresses) I realised I'd made a bit of a mistake with the design of the j and jal instructions. In the current design, these take the 11-bit immediate, shift it left by 1, then replace the bottom 12 bits of pc with the new value, leaving the top 4 bits unchanged. I took this idea from MIPS, but what makes sense (ish) in a 32-bit architecture is very limiting in a 16-bit one. The main problem is that leaving the top 4 bits of pc intact means that you can only jump to addresses if the target is in the same 4KB-sized area of memory. Note that this is not the same as saying within 4KB of the current pc: imagine we're executing at address 0x3ffe, and want to jump to 0x4002. This is only 4 bytes away, but it can't be done with the j instruction as-is, as the j instruction cannot change the top nibble of the address from 0x3 to 0x4.

This isn't terrible in hand-written assembler (indeed, I hadn't even noticed the limitation in all the code I've written so far), but for a compiler it's a royal pain in the backside. When code is emitted, the compiler usually doesn't know exactly what address it will be placed at by the linker, so it can't know whether it's safe to use a j instruction or not.

Instead, I think it makes much more sense to make j and jal PC-relative jumps. The 11-bit immediate will be sign-extended, shifted left, then added to pc+2 just like a branch. In effect, they become unconditional branches with a longer (±2KB) range. This makes them position-independent, which keeps the compiler writer (me!) happy. As a nice side effect, it then greatly simplifies the implementation of both the PC and immediate units of the control boards, as the PC no longer needs to keep track of the top 4 bits, and we don't need a way to set pc directly from the immediate unit, as we'll be going via the ALU to do the add anyway. Coupled with removing the direct link between the exception unit and the PC (by letting the exception unit drive data, and just setting pc from that), this means the program counter is extremely simple.

Details

For my own reference, the changes required are:

  • changing the immediate unit so that J-type instructions are sign-extended;
  • changing PC dramatically, to remove the register holding the old high 4 bits, removing the imm input, removing the exceptidx input, and removing all the multiplexers that chose between them;
  • adding a single bus driver that will output exceptidx shifted left by 1 to the B bus, along with a new control bit in microcode ROM 4 after flgclr
  • add reset and invalid to the inputs of the ctrl_drv signal
  • removing the outputs to the B bus for both pc and flg registers, as we never use these (it's impossible to address these in normal instructions, as they're system registers, and there's no encoding in the regoutb lines that allows us to force output from them). Note that this applies to all system registers, so we could also clean up a lot of the register board at some point