Sunday, November 6, 2022

Graphical Chisel Development Environment with Docker

 In addition to rewriting a large part my processing module project, I've also been trying to streamline the development experience. I had acquired a new laptop and repurposed my old one for a home development server. Both of these had a fresh OS installed, so the project needed to be set up from scratch. This used to be a time-consuming, error-prone process with many steps for installing Chisel, configuring Emacs, and downloading all of the dependencies for building and testing. Downloading dependencies is itself a tricky process that includes opening source files in Emacs and waiting for the language server to start up, so it's difficult to automate. The language server, which enables fast error checking and autocompletion in Emacs, requires many plugins to be loaded, which I would prefer not to have in my default minimal configuration.

I had previously tried out a Docker image made for the Chipyard project, and it was easy to use and included everything needed to produce a System-on-Chip design. I liked that a single download took care of all the dependencies and utilities. It's also important to me that the project setup not leak out onto the rest of my system. The source code itself should remain on the host filesystem, so that every single change is preserved, but everything surrounding it should be contained and able to be reverted quickly. In addition, the Docker instance started up quickly and did not add much overhead to my system.

So, I started following the very good online documentation to write my own Dockerfile that is based on the Chipyard one. It downloads all of the Chisel dependencies and builds the appropriate version of Verilator. Emacs is included along with a specialized configuration and language server plugins so that my editor runs from within the instance. A terminal emulator is also installed to run on startup. This way, nothing more needs to be downloaded when the Docker instance is running; the system is ready to be used completely offline. When launching the instance, two virtual filesystem mounts are created: one for the source code, and one for an X11-related file (/tmp/.X11-unix) so that GUIs can be displayed with the host window manager. This setup has been tested to work successfully on my Linux home server (Ubuntu 21.10) and my main Windows laptop (WSL2).

Now the setup process has become much easier and cleaner, but there are still a few problems. Windows started from the instance don't conform to the OS display scaling settings for my high-DPI displays, so they look tiny by default. One workaround is to increase the Emacs face size. This makes Emacs more readable and keeps it looking sharp, but that is not applicable to gtkwave, the waveform viewer. Another solution for WSL2 is to add a .wslgconfig file to scale the windows to a readable size, but this makes everything look pixelated and blurry (see below).

Another potentially serious problem I ran into early on was the user and permissions associated with files in the source area created from within the instance. By default, the user and group that the instance provides upon launching is root and associates all new files with that user. This includes object files created by git, which can corrupt the database if it was initialized by a non-root user. Fortunately, I was able to recover from this mistake fairly easily, but I changed the Dockerfile to create a user account that can be chosen when building the image. So if the Docker user is the same as the user that launches the image, there is no issue. However, this does requires each individual user to build their own image, which can take around half an hour. The resulting image size is also fairly large (3.69 GB), so downloading it can take a while, not to mention uploading it. Despite these issues, Docker has become part of my routine development process, and I'll continue to improve it for this project and adopt it for future projects as well.