Conditional execution is a cornerstone of computing. I've said it already in the log 21. Conditional execution and instructions.
There is more to it than how the instructions work, though. Where do data come from ?
The decision is often made from the flags in a status register, in the CISC processors. They hold values determined by the result of the precedent instruction(s):
- Carry
- Sign
- Zero
- Overflow
- Parity
- decimal overflow, for BCD-capable processors
- and whatever some guy thought would be cool
Those status flags got a lot of bad press when CISC processors got more elaborate, complicated, pipelined, superscalar... POWER created a pretty elaborate system to overcome this.
MIPS OTOH decided to go the other way and avoid status flags. Just like the GOTO statements were considered harmful 15 years earlier, the RISC canon (borrowing significantly from Cray's designs) simply dropped them altogether. Sure, it simplified many things and speed could increase quite a lot. But the critical datapath from evaluation to decision would greatly increase too and the speed gain would plateau around Y2K or so.
A new middle ground must be found, and some ideas like the "Mill" pop up but mostly, there situation stagnates. And status flags are sufficient for middle-range and low-complexity, moderately slow processors that don't need to scale up.
The #YGREC8 has the minimal 3 status flags (C/S/Z) and the 4th condition is "True" to make it a round, nice 2-bits condition. A 3rd bit negates the condition, and since there was one free bit remaining, I added a further feature : select 4 arbitrary bits as condition sources, in a way that reminds of the RCA1802 (and Analog Devices DSPs) because this is very convenient for an embedded/microcontroller application (you need to make instant decisions from reading the pins). You could reconfigure the source of the "flags" for whatever purpose, like individual bits of a register or peripherals (like FIFO status).
The #YASEP Yet Another Small Embedded Processor dedicates more bits to the conditions : 7 bits, including a register number. With that, you can test a register for the Zero, Sign or LSB bits of a given register. It's pretty RISC because it just reuses the MUX of a pipeline. But there is more :-) The Always and Carry bits can be read in the special case of PC. And you can select one bit out of 16, that can be mapped to a register, peripherals or GPIO.
In the end, conditions are quite simple : you have to reduce something down to a bit, then change execution depending on the bit's value. The CPSZ set is quite easy to get :
- C : Carry is a separate flag. Nice and simple, though this outlier can be quite annoying. And C is not allowing you to use this flag.
- P : Parity. Unavailable in #YGREC8. The way I do it is to get the LSB of the designated source register. You can access with &1. x86 guys thought it would be awesome to compute byte parity (LSB of number of set bits) but I have never seen it used anywhere, even for communications projects. C language doesn't let you access this "feature", so die alone.
- S : Sign. Again, pretty easy : just take the MSB of the designated register (or the last result). Very useful to check for counter wrap-around in counters, for example, and easily coded as >=0 or <0.
- Z : Zero. This is easy : a big OR. But implementations have to take care of the delay.
- in the #YGREC8, the result bus is checked for 0.
- read the whole register in #YASEP Yet Another Small Embedded Processor because you might have loaded a new word from memory.
- F-CPU uses a "shadow" flag that is recomputed everytime the register is written.
Other flags should have an inherent latency because they can become complex, but if you have these 4 flags, you can do anything. It's just a matter of moving data around.
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.