Skip to content
Tech News
← Back to articles

RISC-V: They Should Have Known Better

read original more articles
Why This Matters

This article highlights the limitations of RISC-V's architecture in serving the diverse needs of different computing segments. While RISC-V may dominate low-cost microcontrollers due to its design choices, it is unlikely to replace more specialized architectures in high-performance computing, emphasizing the importance of tailored solutions for different use cases in the tech industry.

Key Takeaways

RISC-V: They Should Have Known Better

Table of Contents

I am often asked to explain my distaste for RISC-V and I often find myself explaining it piecewise. The reactions are often of the form "you just do not understand the brilliance of it all", which is, of course, no argument at all. After being asked for the Nth time to explain, I decided to put it all down in one place so that I could simply link to it when asked next. Plus, if anyone wishes, then, to form a coherent counter-argument, they could refer to my points clearly and in detail by having this text as a reference. All opinions stated here are mine and do not represent the views of my employer, any deity, or my landlord. My cats concurred in part and dissented in part and will publish their opinion later.

Everything for Everyone

RISC-V will own the cheap-as-dirt single-use micro­controller space even­tually. Not due to its ISA design, but despite it.

The first and simplest-to-grasp issue is that one cannot be best for all use cases. RISC-V fans would have you believe that RISC-V will soon own all supercomputers, while also owning all the tiny microcontroller use cases, and all things in between. This is impossible, and would be equally impossible for any ISA. Simply put, the things a high-end CPU needs are diametrically opposed to the things a small cost-saving microcontroller core needs. The design choices are not merely microarchitectural, but actually (and necessarily) impact the CPU architecture itself. For what it is worth, I am 100% sure that RISC-V will own the cheap-as-dirt single-use microcontroller space eventually. Not due to its ISA design, but despite it. It will take this role from 8051 by being an improvement on it -- a bar so low, it is but a speed bump.

What does a cheap microcontroller core need? Let's inspect what they are used for. Typical use cases are to interface with and quickly reconfigure hardware blocks in a larger chip, eg in an MP3 player, an SD card, or a USB stick. The hard work is done by custom IP and the CPU core is just there to occasionally prod a register or configure something. What matters in this case is interrupt latency (lower is better) and size (smaller is better). Usually you would not expect much math to be done on such a core. Mass-produced cost-reduced devices would have the code running out of real ROM (if non-updateable) or RAM (if updateable); NOR flash costs too much and is not an option for really-mass-produced things. When running out of ROM, code size matters because ROMs are not very compact. When running out of RAM, code size matters because SRAMs also take up a lot of space on the die. Thus, code density matters for these use cases. Since much math is not expected, things like hardware dividers (or even multipliers) can be discarded. Privilege separation is also not needed in such single-use situations -- no external untrusted code is expected to ever be fetched. "But, " you might say, "you just described RV32IC (or RV32EC)!"

So, at basically the only purpose such an embed­ded core has, RISC-V is notably worse than the leading existing competi­tor.

Indeed, it is somewhat close, except really you need RV32I_Zicsr to claim that. Without Zicsr, there is no spec-compliant way to handle interrupts, as there is no temporary place to stash a register to allow you to stash the rest of them. MIPS reserved two kegs for this ($k0 and $k1). Without them, RISC-V needs mscratch/sscratch. Without Zicsr, you do not have those and are stuck with weird other methods to do things. And thus we are back in 8051 territory - it specializes in doing things weirdly. Small embedded cores are not out-of-order monsters. If you get one instruction per cycle out of them, you consider yourself lucky. Given this, let's optimistically count the number of cycles needed for an interrupt handler to stash ABI-required regs and call a handler written in C. First we'll use a CSRRW to stash a reg (let's say t0 for ease of explanation) and get a base address of where we may stash the rest. Then we'll need to stash ra, sp, gp, tp, t1-t6 and a0-a7. We'll then need to use another CSSRW to get back the old t0 value and stash that as well. That's at least 21 cycles. On the way out, the math is similar: one CSRRW to read the address of the stashed regs, and 19 loads to load them. That's at least 20 cycles. But that is not all. Since this needs to be done in assembly, we'll need to actually account for the JAL to our C handler and a RET from there. We'll graciously assume those are each two cycles. Thus each interrupt has at least a 44-cycle cost before any work is done in the C handler. Cortex-M0 (the competing cheap 32-bit core) does an interrupt entry in 15 cycles, exit in 12 cycles, and since it pushes the ABI-clobbered regs in hardware, the handler is written in C directly. Thus each interrupt here has only a 27-cycle cost. Oof... that’s a lot faster! You might protest that I am being unfair by not considering RV32E here. By having half as many regs, it can do the initial push 6 cycles faster and the pop as well, bringing its interrupt overhead to 38 cycles. Still over a third more than the Cortex-M0. Oof... So, at basically the only purpose such an embedded core has, RISC-V is notably worse than the leading existing competitor. The existence of CLIC and various proprietary "fast IRQ" / auto-stacking extensions is an additional indictment. The base ISA forces vendors to invent non-standard silicon to reach parity with a decade-old Cortex-M0. That, in turn, further fragments the "standard" (if it can so be called). Hilariously, even with the compressed extension, the typical IRQ prologue is larger and slower than the Cortex-M0’s zero-byte hardware path.

Now, about those compressed instructions. Let us look at them in detail. They are hilariously poorly designed. Say you want to store a byte to a register plus offset. What range of offsets can a 16-bit instruction encode? Zero through three. Not thirty three, not three hundred and three. Three! Well, maybe it is better for storing a halfword? Nope... zero or two. What even? Why? At least when you store a word, you get a sane range of zero through 124 bytes, but what is going on there with those other ones? Worse, the instruction for storing a halfword is encoded similarly to the one storing a byte, but somehow it has fewer options for offsets? Why? Well, one of the bits that store-byte uses for offset is just hardwired to zero... it could have been used to expand the range to at least go to 6, but it doesn't! By comparison, Cortex-M0 is happy to let you use offsets from zero to 31 for bytes, zero to 62 for halfwords, and zero to 124 for words - clearly this covers a lot more use cases. So what happened here? Truly, I do not know, but it is indeed hard to justify. A typical refrain is to just use full-length instructions for these larger offsets. Sure, but density will suffer - the very density that RISC-V fans were bragging about so recently when trumpeting the C extension. But wait, there is more yet. Those instructions to store a byte and a halfword are not even in the C extension. They are in another one called Zcb so you may not get access to them at all, even if their puny range were good enough to use in your situation. We’ll get to "extensions" later...

... continue reading