Skip to content

Session 6 - Synthesis & Physical Design

error_header

Class page | Class recording

Synthesis

I started off by researching and analyzing some synthesis models. I learned the most by reading the chapter A Primer on Digital Circuit Synthesis in the Yosys documentation. I also browsed through a few of the other chapters on the site.

Yosys provides a capable and well-established synthesis engine. In the previous session, when I wanted to see if I could convert my behavioral Verilog into structural Verilog, I had actually already used Yosys for that task.

To better understand how the synthesis process works, I mapped out the core blocks that make up the engine.

synthesis model

Synthesize your design

The class page already had a good step-by-step guide on how to run Yosys. I also found a good demo on the Yosys website. I started the container and opened my UART ALU project from the last session with:

yosys up_dw_alu_uart.v

An interactive shell started with the Yosys header and a parsing message.

 /----------------------------------------------------------------------------\
 |  yosys -- Yosys Open SYnthesis Suite                                       |
 |  Copyright (C) 2012 - 2026  Claire Xenia Wolf <claire@yosyshq.com>         |
 |  Distributed under an ISC-like license, type "license" to see terms        |
 \----------------------------------------------------------------------------/
 Yosys 0.62 (git sha1 7326bb7d6, g++ 13.3.0-6ubuntu2~24.04 -fPIC -O3)

-- Parsing `up_dw_alu_uart.v' using frontend ` -vlog2k' --

1. Executing Verilog-2005 frontend: up_dw_alu_uart.v
Parsing Verilog input from `up_dw_alu_uart.v' to AST representation.
Storing AST representation for module `$abstract\alu_top'.
Storing AST representation for module `$abstract\ctr_up'.
Storing AST representation for module `$abstract\ctr_down'.
Storing AST representation for module `$abstract\alu'.
Successfully finished Verilog frontend.

yosys> 

I could see my four modules, so that looked good. Next I ran help just to see the available commands. It displayed 308 lines. One of the commands is ls, which lists the modules currently loaded.

I then ran:

hierarchy -check -top alu_top
-check
        also check the design hierarchy. this generates an error when
        an unknown module is used as cell type.

-top <module>
        use the specified top module to build the design hierarchy. Modules
        outside this tree (unused modules) are removed.

yosys> hierarchy -check -top alu_top

3. Executing HIERARCHY pass (managing design hierarchy).

4. Executing AST frontend in derive mode using pre-parsed AST for module `\alu_top'.
Generating RTLIL representation for module `\alu_top'.

4.1. Analyzing design hierarchy..
Top module:  \alu_top
ERROR: Module `\uart_tx' referenced in module `\alu_top' in cell `\u_uart' is not part of the design.

I got an error because of the UART library. At first I wasn't sure if I should ignore it this time or investigate it further. I was also curious if I could modify the Verilog code while Yosys was already running.

I found out that I can, but I would have to re-read the file afterwards.

I also realized that I had to read the uart_tx.v library as well.

I copied the library into the directory, opened Yosys again and ran:

read_verilog up_dw_alu_uart.v lib/uart_tx.v

This time the UART module also appeared.

2. Executing Verilog-2005 frontend: lib/uart_tx.v
Parsing Verilog input from `lib/uart_tx.v' to AST representation.
Generating RTLIL representation for module `\uart_tx'.
Successfully finished Verilog frontend.

I then ran the hierarchy command again and got messages like this:

3.4. Analyzing design hierarchy..
Top module:  \alu_top
Used module:     $paramod$f6ca900275f16cbf280bd6b10c3f9b8b97db50c1\uart_tx
Used module:     \alu
Used module:     \ctr_down
Used module:     \ctr_up
Removing unused module `\uart_tx'.
Removed 1 unused modules.

Nothing looked wrong. It looked like it was generating RTLIL, removing unused modules, and renaming some modules.

I also noticed the $paramod$ prefix and looked it up:

-naming_attr <attr>

once a specialization is derived, use the value of the module attribute
<attr> for a name which should be used for the derived module (this
replaces the internal Yosys naming scheme in which the names of derived
modules start with '$paramod$')

Running synthesis

Next step was the actual synthesis.

After the hierarchy step I expected to see some gates, so I ran:

synth -top alu_top

This produced a report of about 1540 lines.

The design hierarchy looked good. Here are some of the things I found:

  • 174 cells

  • 29 flip-flops

  • 49 ANDNOT and 7 NAND gates (At first I thought they were the same, but I found out:)

  • ANDNOT = A & ~B

  • NAND = ~(A & B)

Other notes from the report:

  • No latches detected
  • Async reset detected
  • Final result: Found and reported 0 problems.

Full report here: synth_report.txt

In Yosys stat reprints the whole report while check only shows the last section.

no_latch

Technology mapping

Next I ran two commands:

dfflibmap -liberty /foss/pdks/sky130A/libs.ref/sky130_fd_sc_hd/lib/sky130_fd_sc_hd__tt_025C_1v80.lib

abc -liberty /foss/pdks/sky130A/libs.ref/sky130_fd_sc_hd/lib/sky130_fd_sc_hd__tt_025C_1v80.lib

I got some warnings at this stage, but after checking them it looked normal and safe to continue.

From the ABC output I could see that it was selecting cells such as:

sky130_fd_sc_hd__a21oi_1 cells: 3

The purpose of these steps is:

  • dfflibmap maps sequential logic (flip-flops)
  • abc maps combinational logic (logic gates)

This stage takes generic Yosys primitives like:

$_DFF_PN0_

and maps them to real technology cells like:

sky130_fd_sc_hd__dfrtp_1

from the Sky130 standard cell library.

This was quite a fascinating stage and I was satisfied that the mapping had worked.

abc_yosys

Cleaning and writing the netlist

Next I ran:

clean

This step basically tidies up the netlist and removes wires or cells that are no longer used.

Finally I wrote out the synthesized design:

write_verilog -noattr synth.v

Yosys printed the following messages:

10. Executing Verilog backend.

10.1. Executing BMUXMAP pass.

10.2. Executing DEMUXMAP pass.
Dumping module `$paramod$f6ca900275f16cbf280bd6b10c3f9b8b97db50c1\uart_tx'.
Dumping module `\alu'.
Dumping module `\alu_top'.
Dumping module `\ctr_down'.
Dumping module `\ctr_up'.

Using ls I could see that a new file had been created:

synth.v

The file contains about 1357 lines and represents the final gate-level netlist of the design.

You can find the file here: synth.v

It's also possible to run Yosis with one line:

yosys -p "read_verilog design.v; synth -top top; write_verilog out.v"

Or run it with a TLC script:

yosys -s synth.tcl

Before closing Yosys I ran the check command. The tool reported several warnings about wires that were used but had no driver. Looking closer at it, these were related to unused or partially connected signals in the example modules and did not prevent the synthesis from completing.

It seems like this could likely be cleaned up by running stat again and then using clean. There is also a related command opt_clean.

Since I'm not actually taping anything out and just practicing the synthesis flow, I did not investigate this further.

problems_yosys

Pipeline diagram

To wrap this up I added a diagram of the synthesis pipeline. Seeing the steps visually helped me understand what Yosys is actually doing under the hood, from reading the RTL all the way to producing the final Sky130 gate-level netlist.

pipeline_diag_synth

Place and route flow - OpenROAD

I began by looking at the OpenROAD documentation and the class example. The OpenROAD workflow looks similar to Yosys in the sense that it also runs in an interactive shell. There also seems to be some GUI functionality available, but at this stage I did not explore that yet. I made a quick diagram similar to the one I did for the Yosys flow to get an overview.

openroad_model

There is also an option to run the flow using a .tcl script, but I decided to execute each step interactively to better understand what happens at each stage. I typed openroad in the terminal to open the shell and it printed a rather minimal output:

/foss/designs/s6/uart > openroad
OpenROAD 26Q1-990-g15af3a5c0 
Features included (+) or not (-): +GPU +GUI +Python
This program is licensed under the BSD-3 license. See the LICENSE file for details.
Components of this program may be licensed under more restrictive licenses which must be honored.
openroad> 

The first step was to read the technology files from the Sky130 PDK. This is done with two commands:

read_lef $::env(PDK_ROOT)/sky130A/libs.ref/sky130_fd_sc_hd/lef/sky130_fd_sc_hd.lef
read_liberty $::env(PDK_ROOT)/sky130A/libs.ref/sky130_fd_sc_hd/lib/sky130_fd_sc_hd__tt_025C_1v80.lib

The .lef file (Library Exchange Format) describes the physical layout information of the standard cells.

The .lib (Liberty) file describes the timing and electrical behavior of the cells.

When I ran this command I got a long list of warnings about undefined layers such as li1, met1, pwell, and nwell, as well as warnings about an unknown site called unithd. At first glance the messages looked confusing because they repeatedly contained the word error inside the warning lines.

openroad_warnings

However, OpenROAD also printed the following message at the end:

[INFO ODB-0227] LEF file: /foss/pdks/sky130A/libs.ref/sky130_fd_sc_hd/lef/sky130_fd_sc_hd.lef, created 437 library cells

At this point I decided to pause and check whether this behavior was expected before continuing with the flow. While looking through the documentation and other student examples I noticed that a more complete flow was described in Luis documentation. He encountered the same errors and had worked out a slightly different flow. He had also added the .tlef file in the technology setup.

tlef_li1

I looked inside the sky130_fd_sc_hd__nom.tlef file and could see some information for li1, met1, pwell, and nwell. I didn't find too much information explaining the .tlef itself, but I did find this paper by Jui-Hsin Hung and Jaijeet Roychowdhury. On page 18 it explains:

"Technology Library Exchange Format (.tlef) file: Contains physical properties of all layers/vias and design rules related to their geometry."

Second Route and Flow Attempt

With a new flow reference I began by running these commands:

set design_name alu_top
set top_module  alu_top
set netlist     "synth.v"
set sdc_file    "constraints.sdc"

set out_dir "openroad_out"
file mkdir $out_dir

set pdk_root $::env(PDK_ROOT)

set is used to create a variable and assign a value to it. These variables can then be reused later in the script so paths and file names only need to be defined once.

When running these commands OpenROAD did not print any additional output, which suggested the variables were created successfully.

Next I ran the technology setup:

read_lef $pdk_root/sky130A/libs.ref/sky130_fd_sc_hd/techlef/sky130_fd_sc_hd__nom.tlef
read_lef $pdk_root/sky130A/libs.ref/sky130_fd_sc_hd/lef/sky130_fd_sc_hd.lef
read_liberty $pdk_root/sky130A/libs.ref/sky130_fd_sc_hd/lib/sky130_fd_sc_hd__tt_025C_1v80.lib

This time the commands executed cleanly and the long list of layer warnings from the earlier attempt did not appear. OpenROAD reported that it created 14 routing layers, 25 vias, and 437 standard cells, which confirmed that the technology and library data had been loaded correctly.

After that I loaded the synthesized netlist and linked the top module:

read_verilog $netlist
link_design $top_module

These commands also produced no additional output, which indicated that the design was read and linked successfully.

I had not created the constraints file yet, so I created a minimal constraints.sdc file before continuing. The constraints file only needs to define the clock for the design:

create_clock -name clk -period 20 [get_ports clk]

This defines a clock with a period of 20 ns, corresponding to a 50 MHz clock frequency. Then I ran:

read_sdc $sdc_file

Again there was no output. Next was initializing the floorplan, which defines the space that the design can take.

  • die_area - total chip size

  • core_area - region where standard cells are placed

  • site unithd - the row type used for Sky130 standard cells

to run that I used:

initialize_floorplan \
  -die_area  "0 0 200 200" \
  -core_area "20 20 180 180" \
  -site unithd

I did get a output this time reading this:

[WARNING IFP-0028] Core area lower left (20.000, 20.000) snapped to (20.240, 21.760).
[INFO IFP-0001] Added 58 rows of 347 site unithd.
[INFO IFP-0100] Die BBox:  (  0.000  0.000 ) ( 200.000 200.000 ) um
[INFO IFP-0101] Core BBox: ( 20.240 21.760 ) ( 179.860 179.520 ) um
[INFO IFP-0102] Core area:                        25181.651 um^2
[INFO IFP-0103] Total instances area:              1451.392 um^2
[INFO IFP-0104] Effective utilization:                0.058
[INFO IFP-0105] Number of instances:                    145
openroad> 

The warning told me it had adjusted the size. Looking into it, I learned that this has to do with the placement grid snapping correctly. The rest of the output gave a summary of the die area, core area, and utilization. My design only used a small fraction of the available core area:

  • Core area: 25181.651 um^2
  • Total instances area: 1451.392 um^2
  • Effective utilization: 0.058

I then ran:

make_tracks
place_pins \
  -hor_layers met3 \
  -ver_layers met2

Make tracks gave no feedback and place pins reported this:

Found 0 macro blocks.
Using 2 tracks default min distance between IO pins.
[INFO PPL-0001] Number of available slots 718
[INFO PPL-0002] Number of I/O             9
[INFO PPL-0003] Number of I/O w/sink      9
[INFO PPL-0004] Number of I/O w/o sink    0
[INFO PPL-0005] Slots per section         200
[INFO PPL-0008] Successfully assigned pins to sections.
[INFO PPL-0012] I/O nets HPWL: 927.58 um.

This showed that OpenROAD found 9 top-level I/O pins in the design and placed them successfully around the die boundary. I then ran the next comands:

global_placement
detailed_placement

Global placement outputed a long list with some interesting information in the end:

[INFO GPL-1001] Global placement finished at iteration 409
[INFO GPL-1002] Placed Cell Area             1578.8683
[INFO GPL-1003] Available Free Area         25181.6512
[INFO GPL-1004] Minimum Feasible Density        0.0700 (cell_area / free_area)
[INFO GPL-1006]   Suggested Target Densities:
[INFO GPL-1007]     - For 90% usage of free space: 0.0697
[INFO GPL-1008]     - For 80% usage of free space: 0.0784
[INFO GPL-1009]     - For 50% usage of free space: 0.1254
[INFO GPL-1014] Final placement area: 1578.87 (+0.00%)

Detailed placement also gave a nice summary:

Placement Analysis
---------------------------------
total displacement        275.1 u
average displacement        1.9 u
max displacement            5.3 u
original HPWL            1759.0 u
legalized HPWL           2064.1 u
delta HPWL                   17 %
  • global_placement finds rough positions for the cells

  • detailed_placement snaps them into legal rows and sites

Next was the routing, which is done with:

global_route
detailed_route

global_route took almost no time and gave no feedback, while detailed_route took about 12 seconds to run.

  • global_route: The router decides the rough paths that connections should take across the chip.
  • detailed_route: The wires are placed exactly on the metal tracks and vias according to the technology rules.
[INFO DRT-0267] cpu time = 00:00:12, elapsed time = 00:00:12, memory = 567.96 (MB), peak = 567.40 (MB)

The next and final step was to write the outputs. For that I ran:

write_def $out_dir/${design_name}.def
write_verilog $out_dir/${design_name}_pnr.v

That gave no feedback, but I now had two new files in the directory: alu_top.def and alu_top_pnr.v.

I could now run the reports, which summarized some of the results from going through these steps.

Here are the commands for the reports:

report_design_area
report_checks

I got this output from that:

openroad> report_design_area
Design area 1451 um^2 6% utilization.
openroad> report_checks
Startpoint: u_uart/_226_ (rising edge-triggered flip-flop clocked by clk)
Endpoint: u_uart/_227_ (rising edge-triggered flip-flop clocked by clk)
Path Group: clk
Path Type: max

  Delay    Time   Description
---------------------------------------------------------
   0.00    0.00   clock clk (rise edge)
   0.00    0.00   clock network delay (ideal)
   0.00    0.00 ^ u_uart/_226_/CLK (sky130_fd_sc_hd__dfrtp_1)
   0.38    0.38 v u_uart/_226_/Q (sky130_fd_sc_hd__dfrtp_1)
   0.79    1.17 ^ u_uart/_173_/Y (sky130_fd_sc_hd__nor4bb_1)
   0.37    1.55 v u_uart/_193_/Y (sky130_fd_sc_hd__a2111oi_0)
   0.26    1.81 v u_uart/_197_/X (sky130_fd_sc_hd__and2_0)
   0.22    2.03 ^ u_uart/_203_/Y (sky130_fd_sc_hd__a221oi_1)
   0.00    2.03 ^ u_uart/_227_/D (sky130_fd_sc_hd__dfrtp_1)
           2.03   data arrival time

  20.00   20.00   clock clk (rise edge)
   0.00   20.00   clock network delay (ideal)
   0.00   20.00   clock reconvergence pessimism
          20.00 ^ u_uart/_227_/CLK (sky130_fd_sc_hd__dfrtp_1)
  -0.10   19.90   library setup time
          19.90   data required time
---------------------------------------------------------
          19.90   data required time
          -2.03   data arrival time
---------------------------------------------------------
          17.87   slack (MET)


openroad> 

Doing this step by step took longer than running Yosys, but I'm glad I did it. It gave me a better understanding and will make it easier to use a script in the future.

Layout in KLayout

At this point I wanted to take a look at the layout in Klayout. I imported the alu_top.def and .lef and .tlef. What I saw was not what I expected and I was not sure if I should be impressed or not. I could see some of the routing but no cells.

import_klayout

I found some information on how to make GDS with klayout but that looked a bit complex and I was unable to get it to work. I ended up fiding something called strm2gds that is part of Klayout and works in terminal. I typed it in the terminal and it was there I ran it with help to how it worked since there was not any documentation I could find on-line.

/foss/designs > strm2gds -h
Usage:
  strm2gds  [options]  <input>  <output>
    This program will convert the given file to a GDS2 file 

 Input options - LEF/DEF specific:

    --lefdef-lef-layouts    Layout files for resolving FOREIGN LEF cells from
        This option applies when reading DEF files. 
          Use a comma-separated list of file names here to specify which layout
          files to use for resolving LEF macros. This applies when LEF macros 
          are specified with FOREIGN. By using '--lefdef-macro-resolution-mode'
          you can force external resolution (assume FOREIGN always) or turn it 
          off (ignore FOREIGN).

I ran it a couple of times getting errors but eventually got it to work using this command:

strm2gds alu_top.def alu_top.gds \
  --lefdef-lef-layouts /foss/pdks/sky130A/libs.ref/sky130_fd_sc_hd/lef/sky130_fd_sc_hd.merged.lef,/foss/pdks/sky130A/libs.ref/sky130_fd_sc_hd/gds/sky130_fd_sc_hd.gds

I now had a GDS file that I opened in Klayout. It was nice to see the cells and they were organized into a grid.

klayout_cells

klayout_cells_close

I did notes that even tough this had somewhat worked there was no power grid. So I went to look at the flow and saw that I probably missed that step and also the clock step.

Layout second attempt

Looking back at the script on the class page and what I had been running I notes that I had not ran the clock or power command. This time I made a script and ran it with Openroad. I discovered that open-road has a GUI that you can open with gui::show opening the GUI I could get a clear picture of the layout. I saw the power-rail was still missing and it looked like three cells were overlapping. I used the Tcl interface to rerun the command that was very useful allowing me to see visually what was happening.

openroad_gui

Power Grid

The power grid sems like a critical part to have in the layout so I looked at that first. There were two things that cauth my atention it looked like the global route was not doing any thing and also the pdngen did not seam to work. I was able to find documentaion on the Power Distribution Network Generator and found out it might be missing some arguments and definition. I also notes that the might be a problem with the naming of "VSS and VDD" in sky130 it uses "VPWR and VGND". Was able to find I needed to run this:

tapcell -distance 13 -tapcell_master "sky130_fd_sc_hd__tapvpwrvgnd_1"

This ties the cells to the power grid runnig this made blocks on the floor plan. next I had to define the VDD and VSS naming I did that useing these commands:

set_voltage_domain -power VDD -ground VSS

define_pdn_grid -name "stdcell_grid" -starts_with POWER
add_pdn_stripe -grid "stdcell_grid" -layer "met1" -width 0.48 -pitch 5.44 -offset 0 -extend_to_core_ring

add_pdn_stripe -grid "stdcell_grid" -layer "met4" -width 1.6 -pitch 27.0 -offset 13.5 -extend_to_core_ring

add_pdn_connect -grid "stdcell_grid" -layers {met1 met4}

pdngen

This made the power grid on the floor plan next I made a power ring around the design at this point I was trying to connect the grid to the outside world. I ran these command to make the ring:

add_pdn_stripe -grid "stdcell_grid" -layer "met4" -width 1.6 -pitch 27.0 -offset 13.5 -extend_to_core_ring

add_pdn_ring -grid "stdcell_grid" -layers {met4 met5} \
             -widths {2.0 2.0} \
             -spacings {1.7 1.7} \
             -core_offsets {2.0 2.0} \
             -add_connect

add_pdn_connect -grid "stdcell_grid" -layers {met1 met4}
add_pdn_connect -grid "stdcell_grid" -layers {met4 met5}

pdngen

Now I had a much more complete layout I had outside connection for the I/O pins but not for the power but at least the cells were connected to the power grid.

openroad_gui_pwr

openroad_gui_closeup

At this point I paused a bit I was not sure if I should downscale the Verilog code and run it trough Yosys and OpenROAD again, if I should look into the make file or continue on this design and make a script and change some things like the floor plan size since I was only using a fraction of the space.

I was impressed with the OpenROAD GUI it was a game changer when it comes to debugging and understanding the flow.

Third attempt - Synthesis, route and place

After a bit of thinking I decided to look closer at the class examples there was a make file there that basically just did the whole run in on take. I tried the fortune-tell example and ran make build-fortune this just ran the whole Yosis and open road in a only about 2 minutes. I could then open Klayout with make view-fortune and in Klayout I could open the GDS file and see the cell layout, very impressive work behind this.

fortune_sample_klayout

I looked at the make file and what is was actually doing I though about just renaming my code and put replace it but then I saw the makefile was running two scripts one for Yosys and another one for OpenROAD. I then figured I could just try to use that in my project directory. I decided to start fresh so I made a new project directory with only my Verilog code. I then took the library and flow directories and place them with in my project. The flow scrip had nice comments how to run this so I adapted it to my project. I had to modify the scrip a little just taking the paths with ../ and remove one dot ./

I started by running the synthesis again with:

TOP=alu_top \
VERILOG="up_dw_alu_uart.v ./lib/uart_tx.v" \
OUT_DIR=build \
yosys -c synth.tcl

That ran smoothly and took under a minute I now had a new synth.v file that I could feed into the next step witch was OpenROAD that I ran with:

TOP=alu_top \
OUT_DIR=build \
openroad pnr.tcl

It took a little longer but just a few minutes I kept OpenROAD open this time so I could open the GUI and inspect it after it was done I typed gui::show in the shell and it opened the GUI I was impressed to see a nicely laid out cells with power and every thing needed to make it work.

Layout view

I took time to inspect and view various things in OpenROAD it gives you alot of information. You can look at just the net and from there you can isolate the types of signals. I verified that the clock signal was there. then I looked at the cells I could see some filler cells and then I could see the module cells that were nicely color coded by there role the UART modules took up most of the space.

third_openroad_net

the net layout

third_openroad_cells

cell layout with color code and without the filler cells

third_openroad_input

The I/O connections

Generate GDS

I looked at how the MAKE file made the GDS. It did't look mutch diffrent from what I had seen earlier so I ran the same command again:

strm2gds alu_top.def alu_top.gds \ --lefdef-lef-layouts /foss/pdks/sky130A/libs.ref/sky130_fd_sc_hd/lef/sky130_fd_sc_hd.merged.lef,/foss/pdks/sky130A/libs.ref/sky130_fd_sc_hd/gds/sky130_fd_sc_hd.gds

I opend the GDS file in Klayout and this time it looked a lot better. I could see the power rails, the filler cells and the instance cells. I also looked at the I/O and all the signals were there. I'm not sure how the power rails are connected out side from the chip, it looked like that can just be done by putting a wire on the rails.

third_klayout_gds

3D render

After hours in the terminal and 2D land. I took a break to look at some 3D options Tiny Tape out have a drag and drop version that is pretty intuitive and nice. I did how ever want to try this GDS to .stl that I came across from session 3. It's pretty simple to run I cloned the repo made a virtual environment .venv and ran my files with no problems. It produced 7 .stl files that had one layer. I imported to blender I had to do a little bit of assembly and scale the Z axis. It's not by any means in scale but it was a nice way to visualize how things are routed and placed.

blender_alu_Suzanne

blender_alu_top

Analyze timing reports - identify and fix any violations

I had actually closed OpenROAD, but I had saved a .db file so I could open it again. I realized I had the fortune-tell constraints loaded, so I changed them to this:

create_clock -name clk -period 20 [get_ports clk]
set_clock_uncertainty 0.5 [get_clocks clk]

To get the reports working again I ran this in the Tcl interface:

read_db /foss/designs/s6/alut/alu.db
read_liberty /foss/pdks/sky130A/libs.ref/sky130_fd_sc_hd/lib/sky130_fd_sc_hd__tt_025C_1v80.lib
read_sdc /foss/designs/s6/alut/lib/constraints.sdc

Timing report

To get the timing report I ran report_checks and got these numbers:

  • Data arrival time: 2.03 ns

  • Data required time: 19.40 ns

  • Slack: 17.37 ns (MET)

The slack is the most important number. It should be above zero. If it goes below zero, that means there is a timing violation. I found some good information from the MICRO 2022 by OpenROAD:

"Slack" is the difference between the constraint (0.46ns) and the actual signal propagation time. Positive slack means the constraint is met ("there is extra slack"). Negative slack means the constraint is violated.

So for example, if I had set the constraint to 1 ns, or if the data arrival time had been 20 ns, that would have caused a violation and the slack would have been negative. (MET) means the timing constraint was met. If not, the report would say (VIOLATED).

Slack Meaning
positive timing OK
zero exactly on limit
negative timing violation

Here is the full timing report:

Startpoint: u_uart/_110_ (rising edge-triggered flip-flop clocked by clk)
Endpoint: u_uart/_111_ (rising edge-triggered flip-flop clocked by clk)
Path Group: clk
Path Type: max

  Delay    Time   Description
---------------------------------------------------------
   0.00    0.00   clock clk (rise edge)
   0.00    0.00   clock network delay (ideal)
   0.00    0.00 ^ u_uart/_110_/CLK (sky130_fd_sc_hd__dfrtp_1)
   0.38    0.38 v u_uart/_110_/Q (sky130_fd_sc_hd__dfrtp_1)
   0.79    1.17 ^ u_uart/_057_/Y (sky130_fd_sc_hd__nor4bb_1)
   0.37    1.55 v u_uart/_077_/Y (sky130_fd_sc_hd__a2111oi_0)
   0.26    1.81 v u_uart/_081_/X (sky130_fd_sc_hd__and2_0)
   0.22    2.03 ^ u_uart/_087_/Y (sky130_fd_sc_hd__a221oi_1)
   0.00    2.03 ^ u_uart/_111_/D (sky130_fd_sc_hd__dfrtp_1)
           2.03   data arrival time

  20.00   20.00   clock clk (rise edge)
   0.00   20.00   clock network delay (ideal)
  -0.50   19.50   clock uncertainty
   0.00   19.50   clock reconvergence pessimism
          19.50 ^ u_uart/_111_/CLK (sky130_fd_sc_hd__dfrtp_1)
  -0.10   19.40   library setup time
          19.40   data required time
---------------------------------------------------------
          19.40   data required time
          -2.03   data arrival time
---------------------------------------------------------
          17.37   slack (MET)

Power Report

After analyzing the timing I also generated a power report in OpenROAD using report_power

The report divides the power into three main categories:

  • Internal power – power consumed inside logic gates
  • Switching power – power used when charging and discharging wires
  • Leakage power – static transistor leakage when idle

From the report I got these numbers:

  • Internal power: 1.15e-04 W
  • Switching power: 1.92e-05 W
  • Leakage power: 6.86e-10 W

Total power consumption:

1.35e-04 W (≈ 135 µW)

Group                  Internal  Switching    Leakage      Total
                          Power      Power      Power      Power (Watts)
----------------------------------------------------------------
Sequential             6.03e-05   8.25e-07   3.23e-10   6.11e-05  45.4%
Combinational          2.73e-06   2.25e-06   2.83e-10   4.98e-06   3.7%
Clock                  5.25e-05   1.62e-05   7.99e-11   6.86e-05  50.9%
Macro                  0.00e+00   0.00e+00   0.00e+00   0.00e+00   0.0%
Pad                    0.00e+00   0.00e+00   0.00e+00   0.00e+00   0.0%
----------------------------------------------------------------
Total                  1.15e-04   1.92e-05   6.86e-10   1.35e-04 100.0%
                          85.7%      14.3%       0.0%

Other useful reports

I saw there were several other commands available in the Tcl (~30) and I tried a few.

Command Purpose
report_worst_slack Quickly prints the worst slack value
report_design_area Reports total cell area and utilization
report_clock_latency Reports clock network latency
report_cell_usage Shows which standard cells are used .
report_pulse_width_checks Shows required and actual pulse widths
report_dont_touch Lists objects marked as "dont_touch"

report_floating_nets did give me this warning: [WARNING RSZ-0020] found 2 floating nets.

I learned that reports are definitely a powerful tool for debugging and can give useful information about the design before tape-out.

Reflection

This session left me feeling like I had mapped out a whole cave full of random corners to get lost in. After every assignment I kept thinking: how much deeper does this actually go?

In the end I was glad that the report part was relatively error free and actually much more satisfying than I expected. Seeing something go from an ASCII text Verilog file to silicon n-wells and polysilicon gates, and somewhat understanding how that transformation happens, really makes you appreciate the technology we have around us.

It is also truly amazing to witness more open-source tools like Yosys and OpenROAD becoming established for the future. Seeing this ecosystem grow makes it feel like chip design is slowly becoming more accessible, much like what happened earlier with open-source software and digital fabrication tools.