We take the SERV RISC-V core and map it to 74xx logic with tools that were intended for FPGA/ASIC synthesis. A PCB is sort-of like an ASIC in 127000 nm technology, right?
Before you expect too much:
- The example PCB has been designed for its artistic value. It won't work as-is (see below).
- You can use the tools with your logic (Verilog, VHDL) but you may have to adjust the scripts a bit. In addition, some manual work in KiCad will be required, e.g. add connectors or LEDs.
- The automated part is from Verilog to PCB. All further steps for the example image above are manual work in GIMP and Inkscape. See below for some info about that.
Olof was presenting the serv core at a virtual RISC-V meetup in 2021. He mentioned that someone had converted it to 74xx logic and the gate count was low enough that I just might be able to afford building one. He pointed me to this Twitter thread by ravenslofty. Much of this project is based on their idea.
It would be a lot of work to make a PCB that has enough educational value to warant the cost, e.g. add RAM and peripherals and have some programs that are small enough to somehow load onto the board and then debug with a multimeter.
We revisited the project two years later with a more achievable goal: Make a PCB that looks nice and sort-of could work.
The usual small RISC-V cores need around 1000 LUT. SERV is bit-serial, which is quite unusual for a 32-bit ISA, but it makes the core really small (200 LUT).
Yosys is used by most of the FOSS FPGA flows but you can point it to other targets. Ravenslofty has created such a target description ("74xx-liberty", the main library file is in "liberty" format). Pepijn de Vos has added scripts that convert the resulting netlist into a KiCad schematic.
The algorithms in Verilog to Routing's VPR were meant to place and route logic in an FPGA but they will also unclutter our ratsnest of connections. We only have to provide a description of our, ehm, "FPGA". (It is field programmable if you bring your PineCil, isn't it?)
As this is for artistic value, we choose the aspect ratio of the FPGA grid to fit A4 paper and there are some strategic cutouts.
We are only interested in placement but the result will be better when we let it run routing passes anyway. The tool will generate an XML file that tells us which gates have been combined into one logic element (i.e. one 74xx device in our case) and a text file with the positions of each element in the grid. We generate a KiCad PCB file from that information using SKiDL.
Some parts are best designed in KiCad, e.g. keepout areas for artistic reasons, and buttons+LEDs that should be in specific locations. Therefore, we combine the generated parts with a template PCB.
Next, we need to route the ratsnest. We could try and use the routing that has been generated by VPR but we don't do that, for now. Instead, we let the freerouting auto-router do all of the work. It will finish within an hour or so if the placement is good (i.e. short connections with not too many crossings) and if there is enough space. If you add some huge keepout areas in the middle (why would anyone do that? cough), it will stop making progress after some hours and not finish even after days so it's best to restart with a more relaxed layout in this case.
Sadly, no. And for more than one reason. Most of this is because we ran out of time (and correct function was optional):
- The keepout areas were too much for the auto-router. 200 connections (5%)
are unrouted. It did work with an earlier version (
serv_top
without register file and no keepouts). We think that it will work with more space (and maybe run VPR a few times and see how the auto-router likes each placement). - The register file seems to use a synchronous RAM with heterogeneous widths and the polarity of some control signals doesn't match. We will have to provide a wrapper that adds the required glue logic.
- Instruction and data bus are mapped to DIP switches and LEDs so the processor won't run very far unless you answer its questions for values from the RAM. This is technically a working processor and it sidesteps the issue of how we would bootstrap (i.e. load the initial program into RAM). The downside is that nobody will use it for very long. Any practical PCB would use something like the Servant SoC and add UART and a bootloader in ROM (or a large array of DIP switches - if you have plenty of time and money to spare).
- Crosstalk may be an issue even at small speeds if we inject false edges into the clock or reset lines. Fortunately, these should be the only critical ones so some shielding (or buffers) should help if we run into problems with that. (I don't think freerouting can do shielding but we could increase trace width for the net class and later replace it by a smaller wire plus shields.)
- We haven't simulated the result and what isn't tested usually doesn't work.
I don't expect any significant bugs in the tools but plenty could be hiding
in our scripts and target descriptions.
- We already have models that should work for simulation (see
74_models.v
). - Proprietary ASIC flows use logic equivalence checks (Formality, Conformal). Yosys supports formal verification so we could do the same.
- In either case, we have to map from PCB to Verilog netlist while avoiding doing the same errors as in the forward direction, e.g. swapped pins.
- We already have models that should work for simulation (see
- The image is scaled to A4 but the actual size is 510x353 mm, which is larger than A4 and even A3.
Other improvements that we omitted for lack of time:
- Let VPR determine the order and location or IO pads or add location constraints if the order is fixed.
- Tell VPR about the locations of pins in each logic element. We are currently telling it that pins can be swapped at will, which isn't really true.
- Use the routing info from VPR, e.g. increase grid spacing if routing channels are full. We could even generate wires for the routing so the auto-router has a working starting point for global routing (assuming that we omit connection stubs between routing channels and the actual devices pads because freerouting can probably do that better).
- Automatically call freerouting and import result.
- Install the tools.
- If you have the Nix package manager, enable flakes and run
nix develop
in this repo. - Otherwise, have a look at
flake.nix
and install with a package manager of your choice.
- If you have the Nix package manager, enable flakes and run
- Edit the scripts (optional but recommended):
- Point
kicad/Makefile
to your Verilog and/ or VHDL files.- Let's keep the "serv" name, for now, to keep it simple.
- For VHDL, you will need Yosys with the GHDL plugin.
- Adjust the FPGA grid.
- Change one of the
<fixed_layout>
nodes inkicad/74xx_fpga.xml
and put its name into the Makefile after the--device
parameter for VPR. - Keep the grid only a bit larger than necessary if you want to achieve high utilization (i.e. not so many unused gates).
- Update the grid size argument for
generate_netlist2.py
in the Makefile. You can use the empty string to automatically use min/max occupied positions.
- Change one of the
- Adjust the KiCad template (
kicad/serv_vpr_template.kicad_pcb
).- If you don't want any custom features for you PCB, you can pass an empty
string to
generate_netlist2.py
instead of template file to not use any template. - We will use the auto-router anyway so there's no need to do manual routing for custom parts of the logic.
- If you don't want any custom features for you PCB, you can pass an empty
string to
- Adjust
generate_netlist2.py
to change how the PCB is generated.- Grid spacing is defined in
mm_per_chip_x
andmm_per_chip_y
. - Omit or move the capacitor.
- Adjust position and layer of texts.
- ...
- Grid spacing is defined in
- Point
- Run
make all
(ormake out/serv_vpr_generated.kicad_pcb
). This will generate the PCB. - Run the auto-router.
- Run
make serv-freerouting
to start the auto-router. - Pay attention to board edges. If part of the logic is outside the board, routing will never finish. If you do any manual changes in KiCad, don't forget to export the Spectra file, again.
- Start auto-routing. It will usually need several passes before it has routed everything and then it will start optimization. If it doesn't seem to get any better anymore, left-click on the main area of the window to stop it. It will ask for confirmation and then it needs some time to actually stop.
- Export the Spectra session and import it into KiCad.
- Run
- Do the usual KiCad things, e.g. design rule check, export gerbers, ...
This is not a complete guide because all of this were manual steps with lots of trial and error.
- KiCad: We have added some of the missing trace (that the auto-router could route). That's why the traces on the right look different.
- KiCad: Export -> SVG: F_Cu, B_C, F_Silk
- Cu with colors, Silk in black&white
- (reason not to export in black&white: hole in pads would be invisible (same color as the ring))
- KiCad: Open 3D view. Enable Raytracing. Export screenshots of top and bottom part of the PCB.
- I had set many of the colors to semi-transparent for an earlier experiment but I think that I had undone that for the pictures.
- Load in Inkscape, select everything but background, export "Selection" to PNG with 600 dpi
- I would like to keep it as a vector graphic but Inkscape becomes very slow.
- Import into GIMP:
- invert colors
- select by color with threshold 5%, click light-grey ring of a via, flood-fill with same color as the traces
- add as layers to the same image
- copy content of layer into layer mask, invert colors, use Colors->Curve to make the main color fully opaque
- add a white background layer
- copy bottom and upper part from renderings, adjust scale and position to match copper layers
- Colors and 3D effect for copper layers:
- The geometry is in the layer mask so we can simple fill the whole layer with the desired color.
- Fill F_Cu with Perlin noise: Filters -> Render -> Noise -> Perlin (I haven't written down the settings. Current settings are: alpha 1.2, scale 1.8, z -1.0, iterations 3)
- Change color (done with Colors -> Colorize, I think).
- Fill B_Cu with a darker shade of red.
- Copy F_Cu, rename to "F_Cu shadow", between F_Cu and B_Cu, move down and right by a few pixels.
Fill with a darker shade of red.
- I had the layer mode set to "darken" and that looked a bit better. However, it was only applied to layers in the same group so that became an issue later.
- White background for Silk:
- Layer mask to selection for Silk layer.
- Add a new layer below Silk.
- Grow and/ or feather the selection.
- Fill whole selection with white.
- Repeat with different settings until it looks right.
- Blend between copper and renderings:
- Add silk layers to one layer group and copper layers to another.
- It might make sense to do the same for the renderings (or combine them into one layer).
- Extend rendering layers to image size.
- Edit layer masks. Use gradient fill for the general shape of the blending.
- Use airbrush tool for the details.
- Make the edges of the blend not to straight.
- Darken or remove copper over ICs and capacitors (at least were it looks strange).
- Extend some of the copper traces over the renderings.
- Silk is black in the middle part and white on the renderings. For any text that crosses the blending, make Silk layer opaque for that whole text.
- Add large digits:
- select the wires and/ or areas in KiCad so they are painted in front of other things (e.g. airwires)
- copy screenshot into GIMP
- select wanted parts (select by color)
- copy into main image as a new layer, adjust scale and position
- select by color, feather by 50 px, fill with yellow, feather by 150 px (probably twice), fill with white (or very light yellow), choose sensible opacity for the layer
- This is not just to make the inner ring of the zero visible but also to de-emphasize some tracks that run through the supposed keepout areas (because I had hoped that it would be able to route it without the keepouts but it didn't make much progress because any new trace would have to ripup lots of existing ones). Thus, it should be in front of the copper layers.
- Brighten area for prior work text:
- Add a layer. Rectangular selection. Grow/feather. Fill with white. Set sensible opacity.
- Fixup texts on Silk:
- Some important texts are hidden behind LEDs or missing (wrong layer?).
- We didn't want to redo the renderings so we added texts in GIMP. Font is wrong, perspective is wrong, color doesn't quite match. Well, close enough.
- Add border:
- The copper layers contain the board edge but that doesn't look nice.
- The aspect ratio doesn't exactly match A4 so we will have some white space around our image. The renderings have a defined edge but the copper layers not so much.
- Therefore, add a black border around everything.
- Export PNG
- Use same DPI as before.
- Inkscape:
- New A4 document.
- Add the image and choose sensible page borders so the difference in aspect ratio is not too visible.
- Add texts. The 7-segment font is DSEG.
- Add QR code (
qrencode -t png -o qrcode.png 'https://github.com/BBBSnowball/74xx/tree/hans'
).
- Export text to GIMP:
- This is only because further processing is out of our hands and we want to reduce risk. A PNG seems to be the safest bet with the image being mostly non-vector anyway.
- Select the image. Set page size to content (i.e. the selected image).
- Hide the image (using the "Layers and Objects" dialog).
- Export to PNG using exactly the resolution of the image in GIMP. (DPI won't match because we have scaled the image in Inkscape.)
- Add as a layer in GIMP.
- Export PNG from GIMP.
The resulting XCF file is 500 MB in size so each download would be half of the free, monthly quota on Github. We will have to find some other way to share it...