The EightThirtyTwo ISA – Part 23 – 2021-01-22
It’s been some time since I’ve made any significant changes to the EightThirtyTwo ISA, CPU or compiler backend, and this is mostly because I’ve been busy using it in real projects.
EightThirtyTwo’s original purpose, apart from the obvious learning exercise, was to be the supporting CPU in the AGA Minimig core for the Turbo Chameleon 64, and it’s performed this function quite nicely: it’s significantly smaller than the secondary TG68 used in the ECS core, and somewhat smaller than the OpenRISC cores that were used in some other Minimig variants, while using fewer memory blocks due to its logic-based register file and reasonably good code-density.
Nevertheless there are always bugs to be fixed and improvements to be made.
Recent bugs that required attention included:
- Sometimes accessing stack parameters using the wrong size.
- Confusion when the constant address of a hardware register is already present in r0.
- Using wrong registers for parameters to memcmp().
- The offset in constructs like “.int <somelabel>,offset” was ignored
- and one actual CPU bug: In rare circumstances a construct such as
cond EQ
.lipcrel _target
add r7
mr r0
would end up writing the pc-relative offset to _target to r0, rather than the previous contents of tmp, when the cond block wasn’t processed. This was due to the partial results forwarding I implemented some months back, and the fix was simply to disallow forwarding while the cond flag is set. This had me scratching my head for a while!
The other significant change I’ve made recently has been to the dual-thread mode. I needed some way to signal between threads and allow bootstrap code to load and safely launch a dual-threaded application, so I’ve added a new instruction for this purpose – “sig”.
The semantics are very simple – if the other thread has paused itself using the “cond NEX” instruction, a “sig” instruction on the other thread will wake it.
This instruction is enough to implement simplistic threading primitives, callable from C, and we now have thread_sleep() – puts the current thread to sleep, thread_wake() – wakes up the other thread, and thread_asleep() – which (by way of a flag) can be used to tell whether the other thread is asleep.
Using these primitives I’m now able to bootstrap and run a dual-threaded Dhrystone demo from SDRAM, once again running the two threads concurrently, then reporting their results one at a time – an experiment I’ve wanted to try for a while. (The performance results are disappointing – the two threads are only very slightly more than half the speed of a single-threaded Dhrystone. Gaining a full understanding of why this is will be interesting, but it may be partly due to the simplistic cache I’m currently using – a two-way cache may be better able to keep two threads fed with instructions.)
Apart from the dual thread experiments and bugfixes I’ve been making gradual improvements to the code generator, improving both speed and code density a little, and made one major improvement to the debugger to cut down on the number of core rebuilds or SD-card insertions/removals: the ability to upload a file to RAM over JTAG.
I still have no support for 64-bit integers or floats / doubles – I would like to add both of these in due course.
Thank you Alastair for this insight and for your great work. 🙂