perilus, Part One2025-10-31
TL;DR: I started a new project with my friend Brady
called perilus.
The goal of the project is to design a RISC-V processor. This week I
started learning about Chisel, a hardware construction language embedded
in Scala, and established the project repo. I also learned how to
minimally configure Neovim as something approaching an IDE.
Brady and I want to design a RISC-V processor so we can learn how they work. We’ll probably make multiple attempts, each time learning from our decisions during the one before. I expect that the complexity and performance of the processor will increase over time as well. Our designs will never be comparable to actual chips, but my hope is that we’ll be able to try out interesting ideas and techniques for efficient and secure computation that are entirely unavailable in the software world. Having control at this lowest level has got to be at least somewhat enlightening, and anyway it’s neat.
This project will be pretty open-ended, and we’ll probably just follow our own interests. I’d like to eventually try to realize our design in discrete logic, while Brady is more interested in synthesizing the design for use on an FPGA. Peripherals and coprocessors give us unlimited space to play, to say nothing of the RISC-V ISA extensions. I think we’ll know that the core of the project is done when we can compile Rust for RV32I and expect it to run on our processor without substantial problems.
We decided to take on this project because we’d both received copies of Digital Design and Computer Architecture, RISC-V Edition (hereafter simply “Harris”) from a mutual friend and wanted to apply the concepts in the book to a real system. Our first design will probably be more or less identical to the multicycle processor that the authors present in section 7.4.
I’m also kind of stuck on this idea of a “Software Tree of Life”. The idea goes like this. Every program is ultimately a sequence of machine instructions. Today practically everyone uses a high-level language and a compiler to do the translation, so we abstract away these details because they mostly don’t matter. But the compiler itself is a program, so it was compiled by something. That something was probably an earlier version of the same compiler, which we might view as its parent.
This goes on and on, until eventually you might find that a compiler for a given language was written in a different source language entirely. Far enough in the past you’ll find that the first compiler was written in the assembly language for its platform, and that program has an ancestor that someone probably wrote in machine code directly and entered into memory by hand. This is one of the root nodes in the Software Tree of Life because its parent is a human, or maybe the pen and paper or blackboard where they designed it.
I want to try to create a new root node in this tree.1 Once we have some minimal processor working, even in simulation, I want to write an assembler in machine code by hand, load it into the computer, and then use the assembler to improve it further. Eventually I’ll be able to write a C compiler or FORTH environment in the assembler and make the jump to a “high” level language. After that the universe opens up and we can explore operating systems, scripting, etc. This is all a long way off, of course, but it’s fun to dream.
We’re going to use Chisel for our processor design. Chisel is an embedded language in Scala, which means that it’s actually a library but is so tightly integrated with its host language that it can be seen as a syntax extension. Chisel’s value proposition is pretty interesting. Verilog is the de facto standard hardware description language (HDL), but it doesn’t allow for easy generalization and code reuse, which makes complex designs less flexible and readable. Chisel is a bridge between arbitrary software written in Scala and specific hardware written in Verilog. You build up a representation of the hardware in Scala, where you can do anything that a modern language can do, like taking parameters and looping over collections, and then tell Chisel to emit Verilog that matches that representation with the particular parameters you supplied at runtime.
I don’t know Chisel or Scala, which makes it less attractive for our project, but it seems like Chisel is among the best choices for this kind of work, so it’s probably worth learning. We might find later on that there’s a better option available given our particular skills and knowledge, but until then we might as well try to learn what the cool kids appear to like. We could also try straight Verilog, but honestly I’d rather not, at least for now.
I started by just poking around the Chisel and Scala resources that come up on a basic search. Of these, the Scala Book, the Chisel Bootcamp, and the Chisel Book were the most useful to me. I found the exercises in the bootcamp a little annoying and unclear, but I made it through module 2 and mostly enjoyed it. I think I’ll lean more heavily on the book in future weeks, but I may browse the remaining sections in the bootcamp to get the same information in a less formal style.
I also cloned the Chisel template
repo and copied over a good portion of the file tree to the
perilus2 repo. It’s not at all clear to me
yet how Scala build tooling works or what all the various hidden
directories are for, so I .gitignored what made sense for
the moment. I’m also mostly guessing on the directory structure, but the
template examples seem pretty reasonable. I added a single module (?)
for the processor’s control unit with dangling inputs and outputs whose
names match the figures in Harris, just to get something “on paper”.
vim for a long
time it feels lovely. It’s fast too. Font is Inconsolata from Nerd Fonts.
Brady and I also have a shared interest in learning to use Neovim,
which is the youngest member of the vi/vim
family. Neovim has a large plugin ecosystem, and some people use it as
their primary development environment. This is an attractive idea and
would remove our dependence on VS Code.
This post is not a tutorial, but in any case I mostly followed this
YouTube playlist. My config so far is here. Some of it is
out of date, so be careful to read the installation instructions for any
plugins that you try to use. I don’t think anything in that playlist is
strictly broken, but there are definitely some areas where
improvements to Neovim have made certain plugins or installation methods
unnecessary. In particular, LSP integration appears to be native to
Neovim now, so mason is no longer needed for basic
configs.
I’ve enjoyed my short time with Neovim so far and have mostly not wanted to run back to VS Code. The hardest part was figuring out how to get LSP integration working, but once I realized how simple it is (and how much it had improved since those videos were published) I was pleased. There’s still plenty to improve, but I have a minimally-viable editor now and can do real work in Rust or probably whatever other languages I want to use with it. I’m still getting some of the workflow into my fingers, but I have confidence that I can learn it without too much pain and change it to whatever I want later. It also fits nicely alongside my NixOS config and removes the need for manual extension management in VS Code.
Configuring Neovim was so fun that it ate up about half of my project time this week, which surprises me (but maybe shouldn’t). It seems like plenty of computer people love to spend lots of time making their environment look and work exactly how they want. I used to be pretty dismissive of this kind of thing, which I’m not too proud of, but now I see that it’s a legitimate way to have fun with a computer and can easily improve the moment-by-moment ergonomics of moving bits around. The right kind of control over the appearance or behavior of a machine gives me the same quiet, carefree giddines that I remember from my earliest days of programming. That’s what I felt while playing with Neovim, which is good enough for me by itself.