the actual Verilog source is incredibly small. I would have thought that implementing a CPU, even a toy one, would take more than 500 lines. is this normal for hardware?
At a certain scale, it's conventional for hardware designs to become complex enough that it's necessary to structure them in hierarchies, just to maintain control. This design is small enough that none of the extra structure is essential.
It's possible to be incredibly expressive in Verilog and VHDL. This implementation is written in VHDL, which has an outdated reputation for being long-winded.
Also worth a look: FemtoRV32 Quark [0], which is written in Verilog.
Have you seen the OPC series of CPUs? (One Page Computing - the challenge being to keep the code small enough to be printed onto a single sheet of line printer paper!)
Yup! Thanks for pointing OPC [0] out. These CPUs were a huge eye-opener - and a huge lesson about the value of using a standardized instruction set.
Building a custom CPU commits you to writing an assembler and listing generator - which is a good hobby-project job for one person who's handy with Python. After stumbling through those foothills, though, I found myself at the base of some very steep, scary GCC/binutils cliffs wondering how I could have gotten so lost, so far from home.
Even if all RISC-V does is offer a bunch of arbitrary answers to arbitrary design questions, I consider it a massive win.
Yes, I also found the idea of tangling with GCC (or even llvm) less than appealing. It's not the initial work that puts me off, but the ongoing maintenance cost. For my own project (EightThirtyTwo) I ended up writing a backend for the VBCC C compiler. The downsides are (a) no C++ support, and (b) an unusual license - but the upsides were (a) a build process that takes seconds, not hours, (b) a simple generic RISC backend one can use as a starting point, and (c) a compiler lightweight enough that it could be self-hosting. (I can compile C code for EightThirtyTwo using an Amiga!)
A traditional CPU in its most basic form is nothing more than a programmable state machine where the transition function is the series of instructions you (the programmer) write down, with local state in the form of the register file, and some ports attached to a memory controller (so it can fetch and write instructions and data). A 3-stage fetch/decode/execute pipeline can be done in a very small space if you don't get clever.
This is just that. But nothing more. For example it does not handle any RISC-V CSRs, even the most basic ones. But that's OK: for "computational" machine code kernels that aren't fancy (i.e. basic ALUs get lit up but nothing fancier), you can use software toolchains to emit compatible code like GCC.
A "real" toy CPU i.e. one that won't win awards but can boot something like Zephyr OS or a maybe a miniature OS with some form of memory protection will require many more lines; for proper exception handling, for that memory protection, timers and peripherials, for extra CPU features (atomics, debug interface, whatever.) A comparable CPU for this might be something like PicoRV32, which fits in at about ~2,000 lines of Verilog.
But that's a lot of stuff. Sometimes all you need is a programmable state machine. And with this you can run (limited) normal C programs on it with a supported compiler on a 32-bit machine.
Those two lines are just the VHDL equivalent of #include <stdio.h> - i.e. boilerplate that you'll see in almost every source file.
But it's true that you don't have to describe the ALU down to the bit level - thanks to those two lines you can say "q <= d1 + d2" instead of having to build an adder at the gate level. (Though you can, of course, do that if you really want to!)
Comments
the actual Verilog source is incredibly small. I would have thought that implementing a CPU, even a toy one, would take more than 500 lines. is this normal for hardware?
What you see is all there is.
At a certain scale, it's conventional for hardware designs to become complex enough that it's necessary to structure them in hierarchies, just to maintain control. This design is small enough that none of the extra structure is essential.
It's possible to be incredibly expressive in Verilog and VHDL. This implementation is written in VHDL, which has an outdated reputation for being long-winded.
Also worth a look: FemtoRV32 Quark [0], which is written in Verilog.
[0]: https://github.com/BrunoLevy/learn-fpga/blob/master/FemtoRV/...
Have you seen the OPC series of CPUs? (One Page Computing - the challenge being to keep the code small enough to be printed onto a single sheet of line printer paper!)
Yup! Thanks for pointing OPC [0] out. These CPUs were a huge eye-opener - and a huge lesson about the value of using a standardized instruction set.
Building a custom CPU commits you to writing an assembler and listing generator - which is a good hobby-project job for one person who's handy with Python. After stumbling through those foothills, though, I found myself at the base of some very steep, scary GCC/binutils cliffs wondering how I could have gotten so lost, so far from home.
Even if all RISC-V does is offer a bunch of arbitrary answers to arbitrary design questions, I consider it a massive win.
[0]: https://revaldinho.github.io/opc/
Yes, I also found the idea of tangling with GCC (or even llvm) less than appealing. It's not the initial work that puts me off, but the ongoing maintenance cost. For my own project (EightThirtyTwo) I ended up writing a backend for the VBCC C compiler. The downsides are (a) no C++ support, and (b) an unusual license - but the upsides were (a) a build process that takes seconds, not hours, (b) a simple generic RISC backend one can use as a starting point, and (c) a compiler lightweight enough that it could be self-hosting. (I can compile C code for EightThirtyTwo using an Amiga!)
A traditional CPU in its most basic form is nothing more than a programmable state machine where the transition function is the series of instructions you (the programmer) write down, with local state in the form of the register file, and some ports attached to a memory controller (so it can fetch and write instructions and data). A 3-stage fetch/decode/execute pipeline can be done in a very small space if you don't get clever.
This is just that. But nothing more. For example it does not handle any RISC-V CSRs, even the most basic ones. But that's OK: for "computational" machine code kernels that aren't fancy (i.e. basic ALUs get lit up but nothing fancier), you can use software toolchains to emit compatible code like GCC.
A "real" toy CPU i.e. one that won't win awards but can boot something like Zephyr OS or a maybe a miniature OS with some form of memory protection will require many more lines; for proper exception handling, for that memory protection, timers and peripherials, for extra CPU features (atomics, debug interface, whatever.) A comparable CPU for this might be something like PicoRV32, which fits in at about ~2,000 lines of Verilog.
But that's a lot of stuff. Sometimes all you need is a programmable state machine. And with this you can run (limited) normal C programs on it with a supported compiler on a 32-bit machine.
I suspect some heavier lifting is done here:
It looks that the VHDL source is about instruction decoding, registers, etc, but does not include things like ALU logic. (I don't know VHDL actually.)Those two lines are just the VHDL equivalent of #include <stdio.h> - i.e. boilerplate that you'll see in almost every source file.
But it's true that you don't have to describe the ALU down to the bit level - thanks to those two lines you can say "q <= d1 + d2" instead of having to build an adder at the gate level. (Though you can, of course, do that if you really want to!)