Skip to content

Up-Down Spiral 0,5 - 1,5

I hit a pretty hard wall after the RTL stage. So I had to reset, scale down, and do an up-down spiral.

Up-down spirals are hard when you have a vision to step up step by step toward your final goal. When the spirals go well it's satisfying because you see your goal come closer and closer. Up-down spirals are challenging mentally and technically, and it can be hard to stay motivated. Planning for them and not being surprised is key. It did however catch me by surprise. I thought I had a grip on the synthesis flow.

I thought about changing projects but that's always risky, so I made a new plan.

  • scale down the design
    • remove the ROM
    • remove the RAM
    • look for a simpler RISC-V CPU
    • easier to review the source files
    • isolate the problem
  • look for an alternative workflow
    • try making Makefiles
  • do a sanity check project
    • something simple just to try the flow and gain confidence that the tools are working

Scale down

Remove the ROM

I removed the ROM. It was taking a lot of floorplan space in the design and slowing the flow runs. Removing it meant I could debug OpenROAD and Yosys faster and run more iterations while investigating the problem.

Remove the RAM

During earlier runs I had also seen several routing errors related to the RAM module. Since the RAM was not critical for understanding the flow I removed it as well to simplify the design.

This significantly reduced the complexity of the layout and made it easier to focus on debugging the tool flow rather than the system design.

Find another CPU model

Since I had not started testing the ROM and was not utilizing all the features of PicoRV32, I looked for the simplest RISC-V model possible. I found three that looked promising.

After changing the CPU I encountered the same problems in the flow. That suggested the issue was not related to the specific CPU implementation but something deeper in the design or the place-and-route flow.

Look for alternative workflow

By this time I had made some progress getting better routing results and had almost isolated the problem in the OpenROAD flow.

Makefile

I tried creating a Makefile to better organize the different steps in the flow. However, while experimenting I started modifying things that were probably not intended in the container environment.

At that point I realized the container setup itself might be part of the failure point. Rather than continuing down that path I decided to stop and treat it as a dead end.

Do a sanity check

At this stage I was getting pretty exhausted running flow after flow. Each run took time and most of the time I was just waiting for another error, expecting maybe a small chance that it would finally work.

To reset I created a very minimal design. I removed all the modules except the top module, removed the bus, connected it directly to the GPIO, and ran the flow again.

module simple_rom (
    input [31:0] addr,
    output [31:0] rdata
);

reg [31:0] rom [0:255];

initial
    $readmemh("firmware.hex", rom);

assign rdata = rom[addr[9:2]];

endmodule

This time the run completed smoothly and placement and routing finished correctly.

Running this minimal design was an important sanity check. It showed that the toolchain itself was capable of completing the flow and it gave me confidence that I was starting to understand what the tools were actually doing.

Returning to the flow

After the sanity check design completed I returned to the original design and started reconfiguring the flow step by step.

The simplified project had shown that the OpenROAD toolchain itself was capable of completing a full run. That meant the remaining problems were most likely related to the design complexity or how the flow config.

cells on cells

During this stage I began inspecting the errors more closely, especially the placement stage using the OpenROAD GUI. In earlier runs the placement had sometimes looked chaotic or unfinished which felt like a step-back since that had not been a problem in spiral one but after adjusting the flow the global placement began to place more cleanly.

However, the design still failed later in the flow. The most common failure occurred during detailed placement, where OpenROAD reported that many instances could not be legalized into valid rows. When that happened the flow stopped before routing could complete.

At this point I started experimenting with different flow parameters and investigating the logs more carefully to understand where the failure was happening.

Although the full design still did not complete routing, the progress made during this spiral was making me understand things better. The placement stage was now behaving much more predictably, and the ffailures were easier to isolate. Each time I ran the flow it took time, so I began looking for other ways I could solve the problem.

Alternative RTL to GDS flows

When looking back at the OpenROAD flow and considering whether I should give the Makefile approach another chance, I came across OpenLane. I had heard it mentioned before but did not really know what it was about. I checked the container to see if it was available in the toolchain. I ran this and got this message:

/foss/designs > openlane --version
[INFO] OpenLane2 has been depreciated.
[INFO] Please use LibreLane (start with <librelane>).

Since I did not know much about OpenLane, seeing that it was marked as deprecated was confusing. The OpenLane GitHub page was still active and it was not immediately obvious whether it was still possible to use it or not. The page mentioned that it was in maintenance mode, so I tried running it anyway, but it did not work.

I decided to look at LibreLane, which was also available in the container when I ran --version. The LibreLane GitHub page had instructions that looked fairly straightforward.

python3 -m librelane --pdk-root <path/to/pdk> </path/to/config.json>

However, I was not immediately sure how the JSON configuration file worked or where to find an example. I then found this helpful Newcomers’ Tutorial. I tried creating a basic JSON configuration file, but it did not work on the first attempt. After reading more about the configuration options in the documentation (configuration reference) and experimenting a bit, I eventually ended up with the following configuration:

{
  "DESIGN_NAME": "soc_top",
  "VERILOG_FILES": [
    "dir::src/soc_top.v",
    "dir::src/femtorv32_quark_bicycle.v",
    "dir::src/simple_rom.v",
    "dir::src/gpio.v"
  ],
  "CLOCK_PORT": "clk",
  "CLOCK_PERIOD": 20.0,
  "BASE_SDC_FILE": "dir::constraints.sdc",
  "PNR_SDC_FILE": "dir::constraints.sdc",
  "SIGNOFF_SDC_FILE": "dir::constraints.sdc",
  "FP_CORE_UTIL": 40,
  "PL_TARGET_DENSITY": 0.45,
  "PDK": "sky130A",
  "STD_CELL_LIBRARY": "sky130_fd_sc_hd"
}

Since I was still experimenting with the manual flow in parallel, I duplicated the project repository and slightly restructured and renamed it for the LibreLane experiment. The JSON configuration file was placed in the root of the project directory. I also learned that I could run LibreLane directly from within the project directory using a simple command:

librelane config.json

When I ran it I was immediately impressed. The tool produced very clear and readable terminal output, with structured tables and detailed information about each step of the process. The first two runs failed due to small directory issues, but the error messages were clear and easy to understand, and only minor adjustments were needed.

librelane

Third time's the charm

When I ran LibreLane for the third time it really began to work. The other two runs had been short and failed early, but this time it kept going. The terminal output was impressive: neatly organized tables, readable logs, a progress bar and even a timer. This is life.

It ran for about 15–20 minutes and I mostly just stared at it, starting to feel a little hope. Even if it failed, I now had a lot of human-readable logs to work with.

librelane_timer

Then it finished.

I was honestly a little afraid to check if anything had actually been produced. After looking through the terminal output and not seeing any major errors, it was time for a simple ls.

And wow.

librelane_struct

There were around 80 files this time and a clearly structured directory layout. One directory was named final, and inside it there was another directory called gds. Inside that was the file:

soc_top.gds

librelane_gds

I wasted no time and opened it in the Tiny Tapeout GDS 3D viewer. To my relief it loaded immediately and the design appeared fully routed and clean.

tiny3dgds

Seeing a complete GDS layout generated from just a handful of Verilog files still feels a little surreal. In about fifteen minutes the tools had taken a few text files describing digital logic and transformed them into a physical chip layout made of transistors, wells, and metal layers on the nanometer scale.

I then did a closer inspection in OpenROAD GUI

openroad_routed

openroad_closeup_gnd