ASIC 101, a documentation site on digital ASIC design for beginners
ASIC stands for Application Specific Integrated Circuit. This course will walk you through the process of creating and designing your very own first ASIC chip.
This book will provide interactive tutorials, quizzes and exercises for you to complete. For interactive lesson portions, we will mainly reference problems from ASIC tutor. If you are a Stone Arch Silicon Member, there will be small self led deliverables which correspond to onboarding, feel free to follow along if you wish.
About ASIC
So what even is an ASIC chip? Unlike general purpose processors, it is a chip built to do one job. Compared to an FPGA, an ASIC chip cannot be reprogrammed after it ships. The circuit on an ASIC chip is permanent, and wired directly into the silicon. An ASIC is typically much more expensive and time consuming to create compared to an ASIC chip. For many companies, developing their own ASICs requires an upfront cost in the millions! However, with a large market volume, the price per processor is much lower with ASIC chips. ASICs are also capable of being more performant and power efficient due to a low silicon overhead. FPGAs arent perpindicular to ASIC technology however. An engineer can benchmark and test ASIC designs on an FPGA before menufacturing, saving a lot of time and money in the case of an error.
There are three main components of digital design covered in this book.
-
Register Transfer Level (RTL) design:
The RTL portion involves translating high level design requirements into a cycle-accurate digital representation. Engineers use Hardare Description Languages (HDLs), to define the logic of a system. The RTL description is the foundational source code for all downstream processes. In this book you will learn how to write verilog HDL.
-
Functional verification:
Verification is the most time intensive and important steps of ASIC design. Verification is dedicated to proving that the RTL implementation strictly adheres to the behavioral specification of the chip. Its really important to get this stage right. Nobody wants to waste all their time and money manufacturing a broken chip! In this portion of the book, you will learn how to verify your designs using open source tools.
-
Place and Route (PNR) / Physical Design:
Placement and routing tools transform the verified RTL code into a physical silicon layout composed of layer drawings and polygons. In this portion of the book, you will learn how design the physical mosfets and metal interconnects on your chip.
Preresiquite Content
This book assumes prior knowledge on boolean logic design, roughly to the same level of EE2301 from the University of Minnesota.
Additionally:
- A linux shell you're comfortable in (wsl is ok)
- Git, and a GitHub account. (Github will be used for submitting SASi deliverables)
Instructions for setting up most of the build environment tools used in this book can be found online if not in the appendix or chapter content.
Words from the authors
ASIC design can be a challenging topic to dive headfirst into. If you are having serious issues, don't be afraid to ask a senior member of your club for help. We were all once beginners, just like your. The most important thing to do when reading this book is to have fun. Learning is fun. Have Fun! - Bear BlinSchauer
Quick Start Tutorial
A self-paced project that walks the entire chip-design flow twice: once fast, on an FPGA, so you have a working design in days instead of weeks; then again for real, through an open-source ASIC flow that ends in a GDSII layout. Both halves build the same 8-bit ALU, so the second half is about the physical-design process, not re-learning the RTL.
Part 1 — Build an 8-bit ALU (FPGA, Vivado)
Specify, write, and verify an 8-bit ALU, then synthesize and implement it in Vivado. You pick one of three adder architectures to build the ALU around:
| Adder | Difficulty |
|---|---|
| Ripple Carry | Beginner |
| Carry Lookahead | Intermediate |
| Carry Select | Intermediate |
Everyone verifies, synthesizes, and implements the same way regardless of which adder they chose, then closes out the part by reading Vivado's timing, utilization, and power reports and writing up what they mean.
Part 2 — RTL to GDSII (open-source ASIC flow)
Take the same verified ALU through an open-source standard-cell flow: install the toolchain, synthesize into real standard cells, floorplan, build a power grid, place, route the clock tree and signal nets, extract parasitics, run static timing analysis, and clear signoff checks (DRC, LVS, antenna, XOR). The part ends with a GDSII layout and a tapeout-readiness checklist.
Prerequisites
- A Linux shell you're comfortable in (WSL is fine)
- Git, and a GitHub account for turning in work
- Verilog at the level of EE 2301 — modules, always blocks, testbenches
Start here
Begin with What an ASIC is, which covers what you'll need before lesson 2 and gets blink, a trivial first module, simulating.
What an ASIC is, and what we're building
An ASIC — application-specific integrated circuit — is a chip built to do one job, with the logic frozen into the silicon at manufacturing time. That's the trade: an FPGA can be reprogrammed after it ships, an ASIC can't, and in exchange the ASIC is smaller, faster, and far cheaper per unit at volume.
ASIC 101 walks the whole path once, end to end, on a design small enough to finish: RTL, simulation, synthesis, place and route, and a GDS file that could actually be taped out.
What you need before lesson 2
- A Linux shell you're comfortable in (WSL is fine)
- Git, and a GitHub account for turning in work
- Verilog at the level of EE 2301 — modules, always blocks, testbenches
The flow, in order
| Stage | You write | The tool produces |
|---|---|---|
| RTL | Verilog | — |
| Simulation | Testbench | Waveforms |
| Synthesis | Constraints | Gate-level netlist |
| Place & route | Floorplan | GDSII layout |
A first module
module blink #(parameter N = 24) (
input wire clk,
input wire rst_n,
output wire led
);
reg [N-1:0] count;
always @(posedge clk or negedge rst_n)
if (!rst_n) count <= 0;
else count <= count + 1'b1;
assign led = count[N-1];
endmodule
Nothing here is ASIC-specific yet — that's the point. The RTL is the same; everything downstream of it changes.
Checklist
- Read this page
- Install the toolchain
-
Simulate
blinkand screenshot the waveform
ASIC 101 project: build an 8-bit ALU
This project is your first complete hardware-design flow in Vivado. You will build one 8-bit ALU, choose one adder architecture, verify it, synthesize it, implement it, and collect timing, utilization, and power results.
Overview
The project has one common specification, but you get to choose how the arithmetic hardware is built.
Everyone completes the same ALU. The only branch is the adder:
| adder | difficulty | go to |
|---|---|---|
| Ripple Carry Adder | beginner | page 4 |
| Carry Lookahead Adder | intermediate | page 5 |
| Carry Select Adder | intermediate | page 6 |
You only need to complete one adder.
After your adder works, everyone returns to page 7 for verification.
Your final repository should contain:
asic_101/
├── rtl/
│ ├── adder8.v
│ ├── alu_core.v
│ └── alu_top.v
├── sim/
│ └── alu_tb.v
├── constr/
│ └── alu.xdc
├── reports/
├── screenshots/
└── README.md
By the end, you should have:
- a working 8-bit ALU
- an automated testbench
- a synthesized Vivado design
- a routed Vivado implementation
- utilization results
- timing results
- power results
- a short engineering interpretation of the results
Prerequisites
- AMD Vivado
- basic Verilog
- a target AMD/Xilinx FPGA part or board
- Git
- prior page: What an ASIC is, and what we're building
You do not need to program a physical FPGA board for this project. Vivado is being used as the synthesis, implementation, timing, and power-analysis environment.
Steps
-
Create a folder named
asic_101. -
Create the directory structure shown above.
-
Open Vivado and select Create Project.
-
Create an RTL Project.
-
Select the FPGA board or part assigned by your university/team.
-
Do not add RTL yet if you have not written it.
-
Continue to the ALU specification on page 3.
Project rules
For the arithmetic block:
- You must choose exactly one adder architecture from pages 4–6.
- Your adder module must be named
adder8. - It must use the interface shown on page 3.
- Do not use
a + binsideadder8. - Do not use a vendor adder IP block.
- The
+operator is allowed inside the testbench to calculate expected answers. - Your chosen adder must perform both ADD and SUB operations inside the ALU.
The rest of the ALU may use normal Verilog operators.
Results
At the end of the project, your README will contain a table like this:
| metric | result |
|---|---|
| Adder architecture | — |
| LUTs | — |
| Flip-flops | — |
| Carry resources | — |
| Worst Negative Slack | — |
| Total Negative Slack | — |
| Total on-chip power | — |
| Dynamic power | — |
| Static power | — |
Do not fill these values in yet.
Checklist
- Created the project folder
- Created the Vivado RTL project
- Selected a target FPGA
- Read the project rules
- Continued to page 3
Questions? Ask in the network Discord.
Build the 8-bit ALU
The ALU is the common project used by everyone in ASIC 101. You will implement eight operations and route ADD and SUB through the custom adder architecture you choose.
Overview
Your ALU accepts two 8-bit operands and a 3-bit operation code.
op | operation | result |
|---|---|---|
000 | ADD | a + b |
001 | SUB | a - b |
010 | AND | a & b |
011 | OR | a \| b |
100 | XOR | a ^ b |
101 | NOT | ~a |
110 | Shift left | a << 1 |
111 | Shift right | a >> 1 |
The ALU also produces four status flags:
zero: result is zeronegative: most-significant bit of the result is onecarry: carry-out from ADD or SUBoverflow: signed two's-complement overflow on ADD or SUB
The core is combinational. A small registered wrapper is added so Vivado has a clean register-to-register timing path to analyze later.
Prerequisites
- prior page: ASIC 101 project setup
- basic combinational Verilog
- understanding of two's-complement subtraction
Steps
1. Use one common adder interface
Whichever adder you choose later, it must use this exact module interface:
module adder8 (
input wire [7:0] a,
input wire [7:0] b,
input wire cin,
output wire [7:0] sum,
output wire cout
);
// your architecture goes here
endmodule
This lets every adder plug into the exact same ALU.
2. Reuse the adder for subtraction
Two's-complement subtraction is
a - b = a + (~b) + 1
That means the same adder can perform both operations.
wire sub;
wire [7:0] b_arith;
assign sub = (op == 3'b001);
assign b_arith = b ^ {8{sub}};
For ADD:
sub = 0
b_arith = b
cin = 0
For SUB:
sub = 1
b_arith = ~b
cin = 1
3. Create rtl/alu_core.v
Use this as the project skeleton:
module alu_core (
input wire [7:0] a,
input wire [7:0] b,
input wire [2:0] op,
output reg [7:0] y,
output reg carry,
output reg overflow,
output reg zero,
output reg negative
);
wire sub;
wire [7:0] b_arith;
wire [7:0] arithmetic_result;
wire arithmetic_cout;
assign sub = (op == 3'b001);
assign b_arith = b ^ {8{sub}};
adder8 u_adder (
.a (a),
.b (b_arith),
.cin (sub),
.sum (arithmetic_result),
.cout (arithmetic_cout)
);
always @* begin
y = 8'h00;
carry = 1'b0;
overflow = 1'b0;
case (op)
3'b000: begin
y = arithmetic_result;
carry = arithmetic_cout;
overflow = (~(a[7] ^ b[7])) & (y[7] ^ a[7]);
end
3'b001: begin
y = arithmetic_result;
carry = arithmetic_cout;
overflow = (a[7] ^ b[7]) & (y[7] ^ a[7]);
end
3'b010: y = a & b;
3'b011: y = a | b;
3'b100: y = a ^ b;
3'b101: y = ~a;
3'b110: y = a << 1;
3'b111: y = a >> 1;
default: y = 8'h00;
endcase
zero = (y == 8'h00);
negative = y[7];
end
endmodule
For subtraction, carry = 1 generally corresponds to no borrow in this two's-complement implementation.
4. Create a registered top level
Create rtl/alu_top.v.
module alu_top (
input wire clk,
input wire rst_n,
input wire [7:0] a,
input wire [7:0] b,
input wire [2:0] op,
output reg [7:0] y,
output reg carry,
output reg overflow,
output reg zero,
output reg negative
);
reg [7:0] a_q;
reg [7:0] b_q;
reg [2:0] op_q;
wire [7:0] y_comb;
wire carry_comb;
wire overflow_comb;
wire zero_comb;
wire negative_comb;
alu_core u_core (
.a (a_q),
.b (b_q),
.op (op_q),
.y (y_comb),
.carry (carry_comb),
.overflow (overflow_comb),
.zero (zero_comb),
.negative (negative_comb)
);
always @(posedge clk) begin
if (!rst_n) begin
a_q <= 8'h00;
b_q <= 8'h00;
op_q <= 3'b000;
y <= 8'h00;
carry <= 1'b0;
overflow <= 1'b0;
zero <= 1'b1;
negative <= 1'b0;
end
else begin
a_q <= a;
b_q <= b;
op_q <= op;
y <= y_comb;
carry <= carry_comb;
overflow <= overflow_comb;
zero <= zero_comb;
negative <= negative_comb;
end
end
endmodule
The input registers launch data through alu_core, and the output registers capture the result on the next rising clock edge. That gives Vivado a meaningful internal timing path.
5. Choose your adder
Now choose one path:
- simplest: Ripple Carry Adder — page 4
- more logic, less serial carry propagation: Carry Lookahead Adder — page 5
- duplicated upper-half arithmetic with a mux: Carry Select Adder — page 6
You only complete one page.
Results
Before moving on, you should have:
rtl/
├── alu_core.v
└── alu_top.v
Your adder8.v comes from whichever adder page you choose.
Checklist
- Understand the eight ALU operations
- Understand how ADD and SUB share one adder
-
Created
alu_core.v -
Created
alu_top.v - Selected one adder architecture
- Continued to page 4, 5, or 6
Questions? Ask in the network Discord.
Option A: Ripple Carry Adder
The Ripple Carry Adder is the simplest adder option. Each bit waits for the carry from the bit before it, making the structure easy to understand and easy to verify.
Overview
A one-bit full adder has three inputs:
a
b
carry-in
and two outputs:
sum
carry-out
The logic is
sum = a XOR b XOR cin
cout = (a AND b)
OR (a AND cin)
OR (b AND cin)
An 8-bit Ripple Carry Adder chains eight of these stages.
cin -> bit 0 -> bit 1 -> bit 2 -> ... -> bit 7 -> cout
The important idea is that a carry may have to propagate through every stage.
Prerequisites
- ALU specification
- Boolean AND, OR, XOR
- one-bit full adders
Steps
1. Create rtl/adder8.v
module adder8 (
input wire [7:0] a,
input wire [7:0] b,
input wire cin,
output wire [7:0] sum,
output wire cout
);
wire [8:0] c;
assign c[0] = cin;
genvar i;
generate
for (i = 0; i < 8; i = i + 1) begin : GEN_FULL_ADDER
assign sum[i] = a[i] ^ b[i] ^ c[i];
assign c[i+1] =
(a[i] & b[i]) |
(a[i] & c[i]) |
(b[i] & c[i]);
end
endgenerate
assign cout = c[8];
endmodule
2. Trace one carry by hand
Try:
a = 01111111
b = 00000001
cin = 0
The result should be
sum = 10000000
cout = 0
Several carry signals must ripple through the design before the final sum settles.
3. Add it to the ALU
Make sure your files are
rtl/
├── adder8.v
├── alu_core.v
└── alu_top.v
Do not rename the module. alu_core.v expects a module named adder8.
4. Continue to the common flow
You are done with the adder branch.
Continue to Verify the complete ALU — page 7.
Results
The Ripple Carry Adder is a useful baseline because it uses a simple repeated structure.
Later, record what Vivado actually maps it into. FPGA synthesis may transform your RTL and use device-specific carry resources, so do not assume the post-synthesis hardware will look exactly like the source code.
Checklist
-
Created
adder8.v -
Did not use
a + binside the adder - Understand how the carry ripples
-
Connected the adder to
alu_core - Continued to page 7
Questions? Ask in the network Discord.
Option B: Carry Lookahead Adder
The Carry Lookahead Adder reduces the amount of strictly serial carry logic by computing carry conditions from propagate and generate signals.
Overview
For bit i, define
p[i] = a[i] XOR b[i]
g[i] = a[i] AND b[i]
p means the stage can propagate a carry.
g means the stage generates a carry regardless of the incoming carry.
For one bit,
c[i+1] = g[i] OR (p[i] AND c[i])
Instead of waiting for each carry sequentially, a lookahead block expands the equations so multiple carry conditions can be evaluated in parallel.
This project uses two 4-bit lookahead blocks to build the 8-bit adder.
Prerequisites
- ALU specification
- Boolean algebra
- propagate and generate signals
Steps
1. Build a four-bit lookahead block
Create rtl/adder8.v and begin with:
module cla4 (
input wire [3:0] a,
input wire [3:0] b,
input wire cin,
output wire [3:0] sum,
output wire cout
);
wire [3:0] p;
wire [3:0] g;
wire [4:0] c;
assign p = a ^ b;
assign g = a & b;
assign c[0] = cin;
assign c[1] =
g[0] |
(p[0] & c[0]);
assign c[2] =
g[1] |
(p[1] & g[0]) |
(p[1] & p[0] & c[0]);
assign c[3] =
g[2] |
(p[2] & g[1]) |
(p[2] & p[1] & g[0]) |
(p[2] & p[1] & p[0] & c[0]);
assign c[4] =
g[3] |
(p[3] & g[2]) |
(p[3] & p[2] & g[1]) |
(p[3] & p[2] & p[1] & g[0]) |
(p[3] & p[2] & p[1] & p[0] & c[0]);
assign sum[0] = p[0] ^ c[0];
assign sum[1] = p[1] ^ c[1];
assign sum[2] = p[2] ^ c[2];
assign sum[3] = p[3] ^ c[3];
assign cout = c[4];
endmodule
2. Build the 8-bit adder from two blocks
Add this below cla4 in the same file:
module adder8 (
input wire [7:0] a,
input wire [7:0] b,
input wire cin,
output wire [7:0] sum,
output wire cout
);
wire carry4;
cla4 u_low (
.a (a[3:0]),
.b (b[3:0]),
.cin (cin),
.sum (sum[3:0]),
.cout (carry4)
);
cla4 u_high (
.a (a[7:4]),
.b (b[7:4]),
.cin (carry4),
.sum (sum[7:4]),
.cout (cout)
);
endmodule
This is a block carry-lookahead design: carry logic is expanded inside each four-bit block, while the carry between the lower and upper blocks still forms one block-level dependency.
3. Test a carry-heavy input by hand
Try:
a = 11111111
b = 00000001
cin = 0
Expected:
sum = 00000000
cout = 1
4. Add it to the ALU
Your files should now be:
rtl/
├── adder8.v
├── alu_core.v
└── alu_top.v
5. Continue to the common flow
You are done with the adder branch.
Continue to Verify the complete ALU — page 7.
Results
Compared with the source structure of a Ripple Carry Adder, the lookahead equations expose more carry logic in parallel.
Do not assume that means it will always win on an FPGA. Vivado is free to optimize both designs for the target device, and dedicated FPGA carry hardware can strongly affect the result.
Your job later is to report what the tool actually produced.
Checklist
-
Created the
cla4block -
Created the top-level
adder8 -
Did not use
a + binside the adder -
Connected the adder to
alu_core - Continued to page 7
Questions? Ask in the network Discord.
Option C: Carry Select Adder
The Carry Select Adder trades extra hardware for reduced waiting on part of the carry path. The upper half computes both possible answers in advance and selects the correct one when the lower-half carry arrives.
Overview
Split the 8-bit addition into two four-bit halves.
The lower half computes normally:
bits 3:0
At the same time, the upper half computes twice:
bits 7:4 assuming carry-in = 0
bits 7:4 assuming carry-in = 1
When the lower-half carry is finally known, a mux selects the correct upper result.
Conceptually:
+--> upper add, cin=0 --+
lower add --> carry ---| |--> mux --> final upper sum
+--> upper add, cin=1 --+
Prerequisites
- ALU specification
- Ripple Carry Adder concept
- multiplexers
Steps
1. Create a four-bit Ripple Carry helper
Create rtl/adder8.v.
module rca4 (
input wire [3:0] a,
input wire [3:0] b,
input wire cin,
output wire [3:0] sum,
output wire cout
);
wire [4:0] c;
assign c[0] = cin;
genvar i;
generate
for (i = 0; i < 4; i = i + 1) begin : GEN_FA
assign sum[i] = a[i] ^ b[i] ^ c[i];
assign c[i+1] =
(a[i] & b[i]) |
(a[i] & c[i]) |
(b[i] & c[i]);
end
endgenerate
assign cout = c[4];
endmodule
2. Compute the upper half twice
Add the top-level module below it:
module adder8 (
input wire [7:0] a,
input wire [7:0] b,
input wire cin,
output wire [7:0] sum,
output wire cout
);
wire [3:0] sum_low;
wire carry_low;
wire [3:0] sum_high_0;
wire [3:0] sum_high_1;
wire carry_high_0;
wire carry_high_1;
rca4 u_low (
.a (a[3:0]),
.b (b[3:0]),
.cin (cin),
.sum (sum_low),
.cout (carry_low)
);
rca4 u_high_if_0 (
.a (a[7:4]),
.b (b[7:4]),
.cin (1'b0),
.sum (sum_high_0),
.cout (carry_high_0)
);
rca4 u_high_if_1 (
.a (a[7:4]),
.b (b[7:4]),
.cin (1'b1),
.sum (sum_high_1),
.cout (carry_high_1)
);
assign sum[3:0] = sum_low;
assign sum[7:4] =
carry_low ? sum_high_1 : sum_high_0;
assign cout =
carry_low ? carry_high_1 : carry_high_0;
endmodule
3. Understand the tradeoff
The upper arithmetic is duplicated.
That means the source design contains more logic than a simple ripple structure, but the upper half does not need to wait before beginning its arithmetic.
The final selection still waits for carry_low.
4. Add it to the ALU
Your files should now be:
rtl/
├── adder8.v
├── alu_core.v
└── alu_top.v
5. Continue to the common flow
You are done with the adder branch.
Continue to Verify the complete ALU — page 7.
Results
Later, inspect whether the duplicated structure survives synthesis and how Vivado maps it to FPGA resources.
Do not assume source-code area equals physical area. Synthesis can remove, merge, or transform logic.
Checklist
-
Created the
rca4helper - Computed both upper-half carry cases
- Added the output mux
-
Did not use
a + binside the adder -
Connected the adder to
alu_core - Continued to page 7
Questions? Ask in the network Discord.
Verify the complete ALU
Before synthesis, prove that the RTL is functionally correct. The testbench checks all 524,288 combinations of a, b, and op.
Overview
There are
256 possible values of a
256 possible values of b
8 possible operations
so the complete ALU input space is
256 × 256 × 8 = 524,288 test vectors
Because this ALU is small, exhaustive simulation is practical.
The testbench is allowed to use normal Verilog arithmetic to calculate the expected result. The design under test is still required to use your custom adder8.
Prerequisites
Complete exactly one adder page:
You should also have:
rtl/adder8.v
rtl/alu_core.v
rtl/alu_top.v
Steps
1. Create sim/alu_tb.v
`timescale 1ns/1ps
module alu_tb;
reg [7:0] a;
reg [7:0] b;
reg [2:0] op;
wire [7:0] y;
wire carry;
wire overflow;
wire zero;
wire negative;
reg [7:0] exp_y;
reg exp_carry;
reg exp_overflow;
reg exp_zero;
reg exp_negative;
reg [8:0] tmp;
integer ia;
integer ib;
integer iop;
integer errors;
alu_core dut (
.a (a),
.b (b),
.op (op),
.y (y),
.carry (carry),
.overflow (overflow),
.zero (zero),
.negative (negative)
);
task check_current;
begin
exp_y = 8'h00;
exp_carry = 1'b0;
exp_overflow = 1'b0;
tmp = 9'h000;
case (op)
3'b000: begin
tmp = {1'b0, a} + {1'b0, b};
exp_y = tmp[7:0];
exp_carry = tmp[8];
exp_overflow = (~(a[7] ^ b[7])) & (exp_y[7] ^ a[7]);
end
3'b001: begin
tmp = {1'b0, a} + {1'b0, (~b)} + 9'd1;
exp_y = tmp[7:0];
exp_carry = tmp[8];
exp_overflow = (a[7] ^ b[7]) & (exp_y[7] ^ a[7]);
end
3'b010: exp_y = a & b;
3'b011: exp_y = a | b;
3'b100: exp_y = a ^ b;
3'b101: exp_y = ~a;
3'b110: exp_y = a << 1;
3'b111: exp_y = a >> 1;
endcase
exp_zero = (exp_y == 8'h00);
exp_negative = exp_y[7];
#1;
if (
(y !== exp_y) ||
(carry !== exp_carry) ||
(overflow !== exp_overflow) ||
(zero !== exp_zero) ||
(negative !== exp_negative)
) begin
if (errors < 20) begin
$display(
"FAIL op=%b a=%h b=%h | y=%h c=%b v=%b z=%b n=%b | expected y=%h c=%b v=%b z=%b n=%b",
op, a, b,
y, carry, overflow, zero, negative,
exp_y, exp_carry, exp_overflow, exp_zero, exp_negative
);
end
errors = errors + 1;
end
end
endtask
initial begin
errors = 0;
a = 8'h00;
b = 8'h00;
op = 3'b000;
// A few recognizable vectors appear at the beginning of the waveform.
a = 8'h01; b = 8'h01; op = 3'b000; check_current;
a = 8'h7F; b = 8'h01; op = 3'b000; check_current;
a = 8'h00; b = 8'h01; op = 3'b001; check_current;
a = 8'hAA; b = 8'h55; op = 3'b100; check_current;
a = 8'h81; b = 8'h00; op = 3'b110; check_current;
a = 8'h81; b = 8'h00; op = 3'b111; check_current;
// Exhaustive verification.
for (iop = 0; iop < 8; iop = iop + 1) begin
for (ia = 0; ia < 256; ia = ia + 1) begin
for (ib = 0; ib < 256; ib = ib + 1) begin
op = iop[2:0];
a = ia[7:0];
b = ib[7:0];
check_current;
end
end
end
if (errors == 0)
$display("PASS: all 524288 exhaustive ALU vectors passed.");
else
$display("FAIL: %0d vectors failed.", errors);
$finish;
end
endmodule
2. Add the simulation source in Vivado
Under Simulation Sources:
- add
sim/alu_tb.v - set
alu_tbas the simulation top - keep
alu_topas the synthesis top
The testbench intentionally instantiates alu_core directly so the combinational logic can be exhaustively checked without worrying about pipeline latency.
3. Run Behavioral Simulation
In Flow Navigator:
Simulation
→ Run Simulation
→ Run Behavioral Simulation
Run until the testbench reaches $finish.
You want to see:
PASS: all 524288 exhaustive ALU vectors passed.
4. Inspect the waveform
Zoom in near the beginning of the simulation.
Find at least:
- one ADD
- one SUB
- one bitwise operation
- one shift
Confirm that the waveform matches what you expect.
5. Save evidence
Save:
screenshots/simulation.png
The screenshot should show:
abopy- the four flags
Also save or screenshot the final PASS message.
Do not continue to synthesis with known simulation failures.
Results
Your project should now pass functional verification before implementation.
| verification item | expected result |
|---|---|
| Directed vectors | pass |
| Exhaustive vectors | 524,288 pass |
| ADD/SUB flags | pass |
| Waveform inspected | yes |
Checklist
-
Added
alu_tb.v - Set the correct simulation top
- Behavioral simulation completed
- All 524,288 exhaustive vectors passed
- Saved a waveform screenshot
- Continued to page 8
Continue to Synthesize and implement in Vivado.
Questions? Ask in the network Discord.
Synthesize and implement the ALU in Vivado
Now turn the verified RTL into an FPGA implementation. You will constrain the design, run synthesis, place and route it, and export the reports used for the final analysis.
Overview
Simulation answered:
Does the logic behave correctly?
Synthesis and implementation answer different questions:
What hardware did the tool build?
How many FPGA resources does it use?
Can it meet the requested clock?
What paths limit performance?
What power does Vivado estimate?
For this project, alu_top is the synthesis top.
The registered wrapper creates internal register-to-register timing paths through the ALU.
Prerequisites
- Verification complete
- all tests passing
- target FPGA selected in Vivado
Steps
1. Set the synthesis top
Under Sources:
right-click alu_top
→ Set as Top
Your design sources should contain:
adder8.v
alu_core.v
alu_top.v
Your testbench remains under Simulation Sources only.
2. Add a timing constraint
Create:
constr/alu.xdc
Start with a 100 MHz clock:
create_clock -name clk -period 10.000 [get_ports clk]
The period is in nanoseconds.
10 ns = 100 MHz
Add simple external interface assumptions:
set_input_delay -clock clk 2.000 \
[get_ports -filter {DIRECTION == IN && NAME != clk}]
set_output_delay -clock clk 2.000 \
[get_ports -filter {DIRECTION == OUT}]
These are educational interface assumptions, not board-specific numbers.
If your team provides real board timing requirements, use those instead.
3. Run synthesis
In Flow Navigator:
Synthesis
→ Run Synthesis
When it finishes:
Open Synthesized Design
Inspect:
Schematic
Report Utilization
Look for:
- LUTs
- flip-flops
- carry resources
- whether the adder hierarchy is still recognizable
Save:
screenshots/synthesized_schematic.png
4. Run implementation
In Flow Navigator:
Implementation
→ Run Implementation
Then:
Open Implemented Design
Vivado will place and route the synthesized FPGA resources onto the target device.
You do not need to generate a bitstream for this assignment.
5. Create a reports folder
Make sure this exists:
reports/
If the Vivado working directory is not your repository root, use absolute file paths in the commands below.
6. Export utilization
In the Vivado Tcl Console:
report_utilization -file reports/utilization.rpt
7. Export timing
report_timing_summary -file reports/timing_summary.rpt
Also run:
check_timing -verbose -file reports/check_timing.rpt
8. Export power
With the implemented design open:
report_power -file reports/power.rpt
9. Export a DRC report
report_drc -file reports/drc.rpt
10. Save implementation evidence
Save screenshots of:
screenshots/implemented_device.png
screenshots/timing_summary.png
screenshots/power_summary.png
Your screenshots do not need to contain every number. The .rpt files are the actual source of the final metrics.
Results
After implementation, your repository should contain:
reports/
├── check_timing.rpt
├── drc.rpt
├── power.rpt
├── timing_summary.rpt
└── utilization.rpt
and:
screenshots/
├── simulation.png
├── synthesized_schematic.png
├── implemented_device.png
├── timing_summary.png
└── power_summary.png
Checklist
-
alu_topis synthesis top - Added the clock constraint
- Synthesis completed
- Inspected the synthesized schematic
- Implementation completed
- Exported utilization report
- Exported timing report
- Exported timing-check report
- Exported power report
- Exported DRC report
- Saved implementation screenshots
- Continued to page 9
Continue to Analyze and submit the design.
Questions? Ask in the network Discord.
Analyze and submit the ALU
The final step is to turn Vivado's reports into an engineering summary. Record area proxies, timing, and power, explain what limits the design, and submit the complete repository.
Overview
This page is intentionally PPA-style:
Power
Performance
Area
But remember what you implemented.
Vivado mapped your RTL to an FPGA, not to ASIC standard cells.
That means:
- LUT count is an FPGA area proxy, not ASIC cell area
- Vivado FPGA power is not ASIC power
- FPGA routing and dedicated carry chains affect timing
- the same RTL may behave very differently in an ASIC physical-design flow
The point is to learn how to read implementation reports and reason about hardware tradeoffs.
Prerequisites
- Vivado implementation complete
- all report files exported
Steps
1. Record the adder you chose
In your README:
Adder architecture: Ripple Carry
or:
Adder architecture: Carry Lookahead
or:
Adder architecture: Carry Select
2. Read the utilization report
Open:
reports/utilization.rpt
Record at least:
- LUTs
- flip-flops/registers
- dedicated carry resources if present
Depending on the target device, the exact carry primitive name may differ.
Do not report the number of lines of Verilog as area.
3. Read the timing summary
Open:
reports/timing_summary.rpt
Record:
- requested clock period
- Worst Negative Slack, or WNS
- Total Negative Slack, or TNS
- the startpoint and endpoint of the worst setup path
Interpret WNS:
WNS >= 0 → requested setup timing is met
WNS < 0 → at least one setup path misses the constraint
Then inspect the worst path.
Ask:
- Does it pass through the adder?
- Does it pass through the final ALU output mux?
- Which operation appears to dominate the path?
- Did Vivado map the arithmetic into dedicated carry hardware?
4. Read the power report
Open:
reports/power.rpt
Record:
- Total On-Chip Power
- Dynamic Power
- Device Static Power
- power-analysis confidence level, if shown
Dynamic power is associated with switching activity.
Static power exists even when logic is not actively toggling.
For the base assignment, the normal post-route Vivado power report is enough.
5. Optional: improve power estimation with switching activity
For a more advanced result, generate a SAIF file from simulation and use that switching activity in the power report.
A typical Vivado Simulator flow uses commands such as:
open_saif alu.saif
log_saif [get_objects -r *]
run all
close_saif
Then, with the synthesized or implemented design open, read the activity file before generating power:
read_saif alu.saif
report_power -file reports/power_saif.rpt
Hierarchy names often differ between the testbench and implemented design. If Vivado reports poor SAIF matching, use the appropriate -strip_path when reading the file.
This section is optional unless your team specifically requires activity-based power analysis.
6. Check implementation health
Open:
reports/check_timing.rpt
reports/drc.rpt
Do not blindly ignore warnings.
For each serious warning or error, decide whether it is:
- expected for this educational setup
- caused by an incomplete constraint
- caused by an RTL problem
- caused by a project configuration problem
If the design has unresolved timing or DRC problems, mention them in the README.
7. Create the final results table
Put this in README.md:
## ASIC 101 ALU results
| metric | result |
|--------|-------:|
| Adder architecture | |
| Target FPGA | |
| Clock period | |
| LUTs | |
| Flip-flops | |
| Carry resources | |
| WNS | |
| TNS | |
| Total On-Chip Power | |
| Dynamic Power | |
| Device Static Power | |
| Power confidence | |
8. Write a short interpretation
Under the table, answer these questions in your own words:
- What adder architecture did you choose, and why?
- What is the critical timing path?
- Did the design meet the 100 MHz clock constraint?
- What resource appears most important to the arithmetic implementation?
- How much of the estimated power is dynamic versus static?
- What would you change if you wanted to optimize the design for speed?
- Why are these FPGA measurements not the same thing as ASIC PPA?
Keep this section short. A few sentences or bullets are enough.
9. Final repository structure
Your submission should look approximately like:
asic_101/
├── rtl/
│ ├── adder8.v
│ ├── alu_core.v
│ └── alu_top.v
├── sim/
│ └── alu_tb.v
├── constr/
│ └── alu.xdc
├── reports/
│ ├── check_timing.rpt
│ ├── drc.rpt
│ ├── power.rpt
│ ├── timing_summary.rpt
│ └── utilization.rpt
├── screenshots/
│ ├── simulation.png
│ ├── synthesized_schematic.png
│ ├── implemented_device.png
│ ├── timing_summary.png
│ └── power_summary.png
└── README.md
10. Push to GitHub
Before submitting:
git status
git add .
git commit -m "Complete ASIC 101 ALU"
git push
Make sure the reports and screenshots actually made it into the repository.
Results
A complete ASIC 101 submission demonstrates the entire introductory RTL-to-FPGA implementation loop:
specification
→ RTL
→ custom arithmetic architecture
→ exhaustive verification
→ synthesis
→ implementation
→ timing analysis
→ utilization analysis
→ power analysis
→ engineering interpretation
The next ASIC-specific step is to take verified RTL into a standard-cell physical-design flow and measure actual ASIC area, timing, and power.
Checklist
- Recorded adder architecture
- Recorded LUT and register utilization
- Recorded carry resources
- Recorded WNS and TNS
- Inspected the critical path
- Recorded total, dynamic, and static power
- Checked timing warnings
- Checked DRC warnings
- Completed the README results table
- Wrote the short engineering interpretation
- Included reports
- Included screenshots
- Pushed the final repository
Questions? Ask in the network Discord.
Open-source ASIC flow: RTL to GDSII
This half of ASIC 101 takes the verified ALU from RTL all the way through an open-source ASIC physical-design flow. You will synthesize into real standard cells, create a die and core, build a power grid, place cells, build the clock tree, route metal, extract parasitics, run timing and physical verification, and inspect the final GDSII.
Overview
The flow in the first half of ASIC 101 used Vivado to implement the ALU on an FPGA.
This half targets an ASIC process.
The conceptual flow is:
RTL
↓
lint
↓
logic synthesis
↓
standard-cell netlist
↓
floorplan
↓
power distribution network
↓
placement
↓
clock tree synthesis
↓
global routing
↓
detailed routing
↓
parasitic extraction
↓
static timing analysis
↓
physical verification
↓
GDSII
The main tools are:
| tool | job |
|---|---|
| LibreLane | orchestrates the complete RTL-to-GDS flow |
| Yosys | RTL synthesis and technology mapping |
| OpenROAD | floorplan, placement, CTS, routing, STA, optimization |
| OpenRCX | resistance/capacitance extraction |
| Magic | layout processing, extraction, DRC, stream-out |
| KLayout | layout viewing, stream-out, DRC |
| Netgen | LVS |
| Volare | obtains compatible open-source PDK builds |
Why does this say LibreLane instead of OpenLane?
OpenLane 2 was continued by its original authors as LibreLane. Current installations and commands use:
librelane
The architecture is still the OpenLane 2 lineage, and OpenROAD is still the main physical-design engine.
If you see tutorials using:
./flow.tcl
or:
openlane
check their date before copying commands.
What are we physically building?
We are hardening the alu_top design as a small digital macro.
The result contains:
- a die boundary
- a core area
- rows of standard cells
- combinational cells
- flip-flops
- clock buffers
- tap and endcap cells
- power rails and straps
- signal routing
- vias
- filler cells
- physical verification data
- GDSII geometry
This is not automatically a complete packaged chip.
A standalone tapeout normally also needs top-level integration such as:
- IO/pad cells
- ESD structures
- pad ring or bump planning
- package connections
- foundry-specific signoff requirements
- shuttle-specific wrapper/integration requirements
Think of this project as creating a hardened digital block that could later be integrated into a larger chip.
Prerequisites
- completed ASIC 101 ALU project
- working Verilog for
adder8,alu_core, andalu_top - Git
- Linux, WSL2, or macOS
- at least 8 GiB RAM; 16 GiB is strongly preferred
- comfort using a terminal
Steps
-
Keep your working RTL from the Vivado project.
-
Create a new physical-design directory:
mkdir -p asic_101_gds
cd asic_101_gds
mkdir -p rtl
mkdir -p docs
mkdir -p screenshots
- Copy your RTL into:
asic_101_gds/
└── rtl/
├── adder8.v
├── alu_core.v
└── alu_top.v
- Continue to install the open-source toolchain.
Results
At the end of this section you will have multiple views of the same design:
| view | what it represents |
|---|---|
| RTL Verilog | behavioral hardware description |
| gate-level Verilog | standard-cell connectivity |
| LEF | abstract physical macro view |
| DEF | placed/routed implementation data |
| ODB | OpenROAD database |
| SDC | timing constraints |
| SPEF | extracted parasitic RC |
| SDF | annotated delays |
| Liberty | timing/power models |
| SPICE | transistor/electrical netlist |
| GDSII | final mask-layout geometry |
Checklist
- Understand the complete RTL-to-GDS flow
- Understand that this project is a hardened macro, not automatically a complete packaged chip
- Created the new project directory
- Copied the working ALU RTL
- Continued to page 11
Questions? Ask in the network Discord.
Install LibreLane, OpenROAD, and the ASIC toolchain
Install the complete reproducible open-source ASIC environment. LibreLane bundles compatible versions of Yosys, OpenROAD, Magic, KLayout, Netgen, and the other tools used by the default flow.
Overview
Do not separately install random versions of every EDA tool unless you know exactly why you are doing it.
ASIC flows are sensitive to tool versions.
LibreLane provides a matched environment so:
LibreLane
Yosys
OpenROAD
Magic
KLayout
Netgen
OpenRCX
Volare
work together.
There are three common installation methods:
| method | recommendation |
|---|---|
| Nix | best/reproducible |
| AppImage | easiest on Linux/WSL |
| Docker | fallback |
For the network, Nix is the default path.
AppImage is a good fallback if Nix gives you trouble.
Prerequisites
- RTL-to-GDS overview
- internet connection
- several GB of free disk space
- Linux/WSL2/macOS
Steps
1. Windows users: install WSL2
Open PowerShell as Administrator:
wsl --install -d Ubuntu
Restart if Windows asks you to.
Then check:
wsl --list --verbose
You want Ubuntu to show:
VERSION
2
LibreLane requires WSL2, not WSL1.
Launch Ubuntu from the Start menu.
From this point onward, run Linux commands inside Ubuntu/WSL, not PowerShell.
2. Linux and WSL users: install basic tools
sudo apt-get update
sudo apt-get install -y curl git
3. Install Nix
Do not install Nix with apt.
Use the current Nix installer:
curl --proto '=https' --tlsv1.2 -fsSL \
https://artifacts.nixos.org/nix-installer | \
sh -s -- install --no-confirm --extra-conf "
extra-substituters = https://nix-cache.fossi-foundation.org
extra-trusted-public-keys = nix-cache.fossi-foundation.org:3+K59iFwXqKsL7BNu6Guy0v+uTlwsxYQxjspXzqLYQs=
extra-experimental-features = nix-command flakes
"
Close the terminal completely and open it again.
Why the binary cache?
Without it, Nix may build large EDA tools from source.
The cache lets you download reproducible pre-built tool binaries instead.
4. Clone LibreLane
cd ~
git clone https://github.com/librelane/librelane
Enter the repository:
cd ~/librelane
5. Enter the EDA environment
nix-shell
Your shell prompt should indicate that you are inside a Nix shell.
Every ASIC command in later pages assumes you are inside this environment.
If you leave the shell, enter it again with:
cd ~/librelane
nix-shell
6. Run the smoke test
librelane --smoke-test
The first run may also obtain the supported PDK through Volare.
The smoke test must finish successfully before continuing.
7. Confirm the important tools exist
Run:
librelane --version
yosys -V
openroad -version
klayout -v
magic --version
netgen -batch <<<'quit'
Exact version strings will change over time.
What matters is that the commands execute inside the LibreLane environment.
8. Optional fallback: AppImage
On supported Linux systems:
sudo apt-get update
sudo apt-get install -y \
build-essential python3 python3-venv python3-pip \
python3-tk curl make git
Download the matching LibreLane devshell AppImage from the current LibreLane release page.
Most x86-64 machines use:
librelane-devshell-x86_64.AppImage
Then:
chmod a+x ~/librelane-devshell-$(uname -m).AppImage
~/librelane-devshell-$(uname -m).AppImage
Test it:
librelane --smoke-test
Do not install both methods just because both exist. Pick one working environment.
9. Run the built-in example
Create a scratch folder:
mkdir -p ~/asic_examples
cd ~/asic_examples
Run:
librelane --run-example spm
If the example reaches the end successfully, your machine has completed a real RTL-to-GDS flow.
10. Open the example layout
Inside the folder containing the copied example configuration:
librelane --last-run --flow OpenInKLayout config.json
You should see physical chip geometry rather than RTL source code.
Results
A working installation should provide:
librelane
yosys
openroad
klayout
magic
netgen
and a working Sky130 PDK managed by Volare.
Official documentation:
Checklist
- WSL2/Linux/macOS environment works
- Nix or AppImage environment works
-
librelane --smoke-testpasses - Yosys runs
- OpenROAD runs
- KLayout opens
- Built-in example finishes
- Opened an example layout
- Continued to page 12
Questions? Ask in the network Discord.
Understand the PDK and standard-cell library
Before running the ALU, understand what a PDK actually gives the tools. The RTL is process-independent; the PDK is what turns that logic into geometry and electrical models for a real manufacturing process.
Overview
A PDK, or Process Design Kit, is the interface between chip designers and a semiconductor manufacturing process.
For this tutorial we use:
sky130A
with the default high-density standard-cell library:
sky130_fd_sc_hd
Sky130A contains five main metal routing layers plus local interconnect.
RTL does not contain transistor geometry
This Verilog:
assign y = a & b;
does not tell a foundry:
where to draw diffusion
where to draw polysilicon
how wide metal should be
what spacing is legal
how fast the gate is
how much capacitance it presents
The PDK and standard-cell library provide those facts.
Standard cells
A standard cell is a pre-designed physical implementation of a small logic function.
Examples include:
inverter
NAND
NOR
AND
OR
XOR
multiplexer
buffer
flip-flop
clock buffer
tie cell
tap cell
filler cell
A synthesized ASIC is mostly a large number of these cells placed in rows and connected with metal.
Different views of one cell
The same cell can have several representations.
| file/view | contains |
|---|---|
.lib Liberty | delay, slew, capacitance, power, timing arcs |
| LEF | abstract size, pins, routing blockages |
| GDS | full physical geometry |
| SPICE | transistor-level electrical representation |
| Verilog | functional model |
The tool uses the cheapest useful view for each task.
For example:
- synthesis needs logic/timing information
- placement needs cell dimensions
- routing needs pins and obstructions
- stream-out needs full GDS geometry
- LVS needs connectivity/electrical information
FEOL and BEOL
FEOL, front end of line, creates devices such as transistors.
BEOL, back end of line, creates the interconnect stack above those devices.
A routed digital block contains many BEOL shapes:
metal wires
vias
power straps
clock routes
signal routes
Prerequisites
Steps
1. Find your PDK root
LibreLane/Volare normally manages the PDK under your home directory.
Try:
echo "${PDK_ROOT:-$HOME/.volare}"
You can also inspect:
ls ~/.volare
Do not modify PDK files for this tutorial.
2. Understand sky130A
sky130A is the fully qualified PDK variant name.
Use:
sky130A
not:
sky130
The variant matters because the backend metal stack and extracted interconnect behavior are part of the process definition.
3. Understand the standard-cell library name
The default Sky130 standard-cell library used by the flow is:
sky130_fd_sc_hd
Break the name apart conceptually:
sky130
fd
sc
hd
The important part for this class is that hd is a high-density digital standard-cell library.
4. Look at cell names after synthesis
Later you will see names similar to:
sky130_fd_sc_hd__and2_1
sky130_fd_sc_hd__mux2_1
sky130_fd_sc_hd__dfxtp_1
sky130_fd_sc_hd__buf_2
The suffix often represents a drive-strength variant.
A stronger cell can drive more load or improve slew, but usually consumes more area and power.
This is one of the basic physical-design tradeoffs:
stronger drive
→ possibly faster
→ usually larger
→ usually more power/capacitance
5. Understand PVT corners
Real silicon is not one perfect device.
Timing changes with:
Process
Voltage
Temperature
Together these are often called PVT conditions.
Physical interconnect also has corners, so LibreLane's reports may use fully qualified interconnect/process/voltage/temperature corners.
Do not report only the best corner and pretend it represents every chip.
6. Know what the PDK controls
The PDK provides or constrains things such as:
- legal metal layers
- routing directions
- widths
- spacings
- via definitions
- design rules
- standard-cell dimensions
- timing models
- power models
- RC extraction information
- IO routing layers
- well tap cells
- endcap cells
- filler and decap cells
The PDK is why a layout for one process cannot simply be fabricated in another process.
Results
You should now be able to explain this sentence:
Yosys maps the ALU into the logic cells available in the selected standard-cell library, and OpenROAD physically places and connects those cells using the geometry, routing, timing, and extraction information supplied by the PDK.
Checklist
- Know what a PDK is
- Know what a standard cell is
- Know the difference between Liberty, LEF, GDS, SPICE, and Verilog cell views
- Know the difference between FEOL and BEOL
- Know what PVT means
- Continued to page 13
Questions? Ask in the network Discord.
Configure and run the ALU flow
Create the LibreLane design configuration and run a complete baseline RTL-to-GDS implementation before tuning individual stages.
Overview
LibreLane is a flow orchestrator.
Instead of manually invoking every program, you provide:
RTL
constraints
PDK choice
physical-design settings
LibreLane creates a sequence of steps and passes the design state from one tool to the next.
A configuration file is not HDL.
It describes how the EDA flow should implement the HDL.
Prerequisites
- PDK and standard cells
- working
adder8.v - working
alu_core.v - working
alu_top.v - inside the LibreLane environment
Steps
1. Prepare the project
Your folder should look like:
asic_101_gds/
├── rtl/
│ ├── adder8.v
│ ├── alu_core.v
│ └── alu_top.v
├── screenshots/
└── config.json
2. Create config.json
Start with:
{
"DESIGN_NAME": "alu_top",
"VERILOG_FILES": [
"dir::rtl/adder8.v",
"dir::rtl/alu_core.v",
"dir::rtl/alu_top.v"
],
"CLOCK_PORT": "clk",
"CLOCK_PERIOD": 10.0,
"PDK": "sky130A",
"STD_CELL_LIBRARY": "sky130_fd_sc_hd",
"FP_SIZING": "absolute",
"DIE_AREA": [0, 0, 150, 150],
"CORE_AREA": [10, 10, 140, 140],
"FP_CORE_UTIL": 35,
"PL_TARGET_DENSITY_PCT": 45
}
3. Understand every field
DESIGN_NAME
alu_top
must match the top-level Verilog module.
VERILOG_FILES
tells synthesis which RTL files belong to the design.
CLOCK_PORT
identifies the top-level clock port.
CLOCK_PERIOD
is in nanoseconds.
10 ns = 100 MHz
The period is a constraint, not a promise.
The flow tries to produce a physical implementation that satisfies it.
PDK
selects the manufacturing-process configuration.
STD_CELL_LIBRARY
selects the standard-cell library.
FP_SIZING
set to absolute means we are explicitly choosing physical dimensions.
DIE_AREA
is the outer physical block boundary:
x0 y0 x1 y1
in micrometers.
CORE_AREA
is the region inside the die where standard-cell rows are created.
Our starting geometry is intentionally roomy because the ALU is tiny and we want enough space to clearly inspect the PDN and routing.
FP_CORE_UTIL
is still useful as an educational reference for utilization-oriented experiments, although explicit die/core geometry fixes the available physical region.
PL_TARGET_DENSITY_PCT
controls the target cell-placement density.
Do not optimize these numbers yet.
First make a baseline run.
4. Run the full flow
From the project directory:
librelane config.json
The default flow is Classic.
This is equivalent to explicitly writing:
librelane --flow Classic config.json
5. Watch the step names
You should see steps corresponding to stages such as:
Verilator lint
Yosys synthesis
pre-PNR STA
floorplan
tap/endcap insertion
PDN generation
global placement
IO placement
detailed placement
CTS
global routing
antenna checking/repair
detailed routing
RC extraction
post-PNR STA
IR-drop reporting
GDS stream-out
DRC
LVS
manufacturability checks
The exact numbering can change between releases.
The step names matter more than the numeric prefixes.
6. Find the run directory
After the run:
ls runs
A run directory will contain individual step folders.
A typical run contains:
runs/<run-tag>/
├── 01-...
├── 02-...
├── ...
├── final/
├── error.log
├── info.log
├── warning.log
└── resolved.json
7. Find the final views
find runs -path "*/final/*" -maxdepth 4 -type f | head -50
The final directory may contain:
def/
gds/
lef/
lib/
nl/
odb/
pnl/
sdc/
sdf/
spef/
spice/
metrics.csv
metrics.json
8. Open the final layout in KLayout
librelane --last-run --flow OpenInKLayout config.json
9. Open it in OpenROAD
librelane --last-run --flow OpenInOpenROAD config.json
KLayout is excellent for viewing final geometric layers.
OpenROAD is better for understanding the physical-design database, timing paths, cells, nets, congestion, and routing.
10. Save a baseline screenshot
Save:
screenshots/baseline_gds.png
Do not worry if it looks mostly like colored rectangles right now.
The next pages explain what every major structure is.
Results
You should now have your first ASIC implementation of the ALU.
Do not call the project finished merely because a .gds file exists.
You still need to understand:
- what synthesis built
- what the floorplan means
- how power is delivered
- why cells are where they are
- how the clock is distributed
- where routing congestion appears
- whether timing closes
- whether DRC/LVS/signoff checks pass
Checklist
-
Created
config.json - Understand every configuration field
- Full flow completed
- Located the run directory
- Located final GDS
- Opened KLayout
- Opened OpenROAD
- Saved baseline screenshot
- Continued to page 14
Questions? Ask in the network Discord.
Synthesis: turn RTL into standard cells
Synthesis converts the behavioral RTL into a gate-level network and maps that network onto cells from the Sky130 standard-cell library.
Overview
Start with RTL:
always @(posedge clk) begin
a_q <= a;
end
After synthesis, the design contains an actual library flip-flop cell.
Start with:
assign y = a ^ b;
After synthesis, the logic may become:
- XOR cells
- NAND/NOR combinations
- AOI/OAI cells
- mux cells
- some other equivalent network
Synthesis is allowed to transform logic as long as the resulting circuit is logically equivalent under the design constraints.
Three conceptual synthesis phases
RTL elaboration
→ generic Boolean/sequential network
→ technology mapping
Why your adder source may not survive literally
You chose an architecture such as:
Ripple Carry
Carry Lookahead
Carry Select
That architecture expresses your RTL intent.
Yosys and its technology-mapping passes may:
- flatten hierarchy
- propagate constants
- simplify Boolean expressions
- remove redundant logic
- remap functions
- resize/restructure logic later in PNR
Therefore:
compare the implemented results, not just the number of operators you typed.
Prerequisites
Steps
1. Locate the synthesis step
Find it without relying on a hard-coded step number:
RUN=$(ls -dt runs/* | head -1)
find "$RUN" -maxdepth 1 -type d -name '*yosys-synthesis*'
Store the directory:
SYNTH_DIR=$(find "$RUN" -maxdepth 1 -type d -name '*yosys-synthesis*' | head -1)
echo "$SYNTH_DIR"
2. Inspect synthesis output files
find "$SYNTH_DIR" -maxdepth 1 -type f -printf '%f\n' | sort
Look for:
log files
netlists
reports
state_out.json
3. Search the mapped netlist for Sky130 cells
grep -R "sky130_fd_sc_hd__" "$SYNTH_DIR" | head -40
You should see standard-cell instance names.
4. Count cell-name occurrences
This is only a rough text-level exploration, not the authoritative area report:
grep -Rho "sky130_fd_sc_hd__[A-Za-z0-9_]*" "$SYNTH_DIR" \
| sort \
| uniq -c \
| sort -nr \
| head -30
Later OpenROAD/metrics reports provide more reliable physical counts and area.
5. Find the synthesis statistics
Search:
grep -RniE "area|cells|wires|ABC|stat" "$SYNTH_DIR" | head -80
Look at the Yosys log and identify:
- number of cells
- cell types
- sequential elements
- combinational elements
- area estimate if reported
6. Compare RTL and netlist
Ask:
Can I still recognize the adder hierarchy?
Did synthesis flatten it?
What cell implements the input/output registers?
Are there explicit XOR cells?
Are there mux cells?
Were some operations combined?
7. Understand the clock before CTS
At this stage the design has clocked cells, but the physical clock tree has not been built yet.
Synthesis knows:
these flip-flops are driven by clk
It does not yet know the final physical clock-buffer locations and routed clock wires.
8. Understand area at this stage
Synthesis area is mainly:
sum of standard-cell areas
It is not final die area.
Final physical area also needs:
- whitespace
- placement rows
- tap/endcap cells
- clock buffers
- routing resources
- PDN
- physical margins
- possibly decap/filler cells
9. Save evidence
Save a short text excerpt or screenshot showing mapped Sky130 cells:
screenshots/synthesis_cells.png
Results
You should now understand the boundary:
RTL describes function
synthesis chooses a gate-level implementation
physical design assigns real positions and wires
Checklist
- Located the Yosys synthesis step
- Found a mapped netlist
- Found Sky130 standard-cell names
- Identified flip-flop cells
- Inspected synthesis statistics
- Understand why RTL architecture and final physical implementation may differ
- Continued to page 15
Questions? Ask in the network Discord.
Floorplanning: die, core, rows, pins, taps, and endcaps
Floorplanning creates the physical canvas on which the design will be built. This is where abstract logic first receives a real physical boundary.
Overview
Two rectangles matter immediately:
+----------------------------------+
| DIE |
| |
| +--------------------------+ |
| | CORE | |
| | | |
| | standard-cell rows | |
| | | |
| +--------------------------+ |
| |
+----------------------------------+
Die area
The die area is the outer boundary of the block being hardened.
In this project:
"DIE_AREA": [0, 0, 150, 150]
means approximately:
150 µm × 150 µm
for the block boundary.
Core area
The core area is the inner region where standard-cell rows are created.
"CORE_AREA": [10, 10, 140, 140]
creates margins around the core.
Why not fill the entire die with cells?
Physical design needs room for:
- routing
- power distribution
- clock buffers
- timing-fix buffers
- legal placement movement
- congestion relief
- physical-only cells
A design with no whitespace can become impossible to route or impossible to fix.
Placement sites and rows
Standard cells are not placed at arbitrary coordinates.
They sit on legal sites arranged into rows.
Cells are designed to align so neighboring cells can share power rails and follow the library's placement geometry.
Tap cells
CMOS wells/substrate regions must be tied to the correct supply potentials.
Tap cells provide legal well/substrate connections at required intervals.
They are physical infrastructure, not part of your Boolean function.
Endcap cells
Endcaps protect/legalize the ends of standard-cell rows according to library/process requirements.
Again, they are physical-only cells.
Prerequisites
Steps
1. Locate the floorplan state
RUN=$(ls -dt runs/* | head -1)
FP_DIR=$(find "$RUN" -maxdepth 1 -type d \
-name '*openroad-floorplan*' | head -1)
echo "$FP_DIR"
2. Open the floorplan in OpenROAD
librelane \
--with-initial-state "$FP_DIR/state_out.json" \
--flow OpenInOpenROAD \
"$RUN/resolved.json"
3. Identify the die boundary
In the GUI, find the outer block rectangle.
This is not a decorative border.
It is the physical boundary used by the implementation.
4. Identify the core
Look inside the die for the region containing placement rows.
Ask:
How much margin exists between the core and die?
Why might routing or PDN need that margin?
5. Inspect rows
Zoom in until you can see the repeated standard-cell row structure.
Rows generally alternate orientation so adjacent rows can share power rails correctly.
6. Find the tap/endcap stage
TAP_DIR=$(find "$RUN" -maxdepth 1 -type d \
-name '*tapendcapinsertion*' | head -1)
echo "$TAP_DIR"
Open it:
librelane \
--with-initial-state "$TAP_DIR/state_out.json" \
--flow OpenInOpenROAD \
"$RUN/resolved.json"
Now compare this with the raw floorplan.
Look for physical-only cells added to the rows.
7. Understand utilization
Core utilization is conceptually:
standard-cell area
------------------ × 100%
usable core area
Higher utilization means:
more cells packed into less area
That can improve compactness, but can also cause:
routing congestion
poor timing
insufficient room for buffers
harder legalization
Lower utilization usually means more whitespace and a larger core.
8. Understand aspect ratio
An aspect ratio near:
1.0
produces a roughly square core.
A rectangular core may be useful for:
- interface geometry
- macro placement
- top-level integration
- routing structure
- package constraints
There is no universal ideal shape.
9. Measure dimensions
Record:
die width
die height
core width
core height
Calculate:
die area = width × height
core area = width × height
Use µm and µm².
10. Save screenshots
Save:
screenshots/floorplan.png
screenshots/tap_endcap.png
Results
Record:
| floorplan metric | value |
|---|---|
| Die width | |
| Die height | |
| Die area | |
| Core width | |
| Core height | |
| Core area | |
| Aspect ratio | |
| Target utilization/density |
Checklist
- Can identify die boundary
- Can identify core boundary
- Can identify placement rows
- Understand placement sites
- Understand tap cells
- Understand endcap cells
- Understand utilization
- Recorded dimensions
- Continued to page 16
Questions? Ask in the network Discord.
Power distribution: VDD, ground, rails, straps, and IR drop
The power distribution network, or PDN, delivers current from the block's power connections to every standard cell.
Overview
Logic gates cannot function without physical power.
A chip does not receive ideal 1 and 0 values.
It receives electrical supply networks:
VDD / VPWR
ground / VGND
The PDN often has a hierarchy:
upper metal straps
↓
lower-level power distribution
↓
standard-cell rails
↓
transistor source/body connections
Signal routing and power routing are different jobs
Signal routing connects logic nets:
adder carry
mux select
result bit
clock
Power routing distributes current to a huge number of cells.
A PDN needs:
- enough metal width
- enough connections
- appropriate pitch
- adequate via connections
- low enough resistance
- acceptable voltage drop
IR drop
Any real conductor has resistance.
Voltage drop is approximately:
V = I × R
If a region draws current through too much resistance, the local supply voltage falls.
That can:
- slow cells
- worsen timing
- reduce noise margin
- cause functional failure in severe cases
Prerequisites
Steps
1. Find the PDN generation stage
RUN=$(ls -dt runs/* | head -1)
PDN_DIR=$(find "$RUN" -maxdepth 1 -type d \
-name '*generatepdn*' | head -1)
echo "$PDN_DIR"
2. Open the design after PDN generation
librelane \
--with-initial-state "$PDN_DIR/state_out.json" \
--flow OpenInOpenROAD \
"$RUN/resolved.json"
3. Hide signal layers selectively
Use the OpenROAD GUI layer controls.
Toggle metal layers on and off.
Find the repetitive VDD/ground structures.
Zoom in enough to distinguish:
power rails
power straps
vias
4. Follow one power path conceptually
Pick a standard-cell row and answer:
How does this row reach VDD?
How does it reach ground?
Which metal layers carry the larger PDN structures?
Where are vias used to change layers?
5. Understand vias
A wire on one metal layer does not automatically connect to a crossing wire on another layer.
A via is required for an electrical connection between layers.
In dense current paths, multiple vias may be used to reduce resistance and improve reliability.
6. Understand why upper metal is useful
Higher metal layers are often better suited to long-distance distribution because the process may provide different width/pitch/resistance options on those layers.
The exact layer usage is PDK- and flow-dependent.
7. Find the IR-drop report
The default Classic flow includes an OpenROAD IR-drop reporting step when enabled.
Find it:
find "$RUN" -maxdepth 1 -type d -iname '*irdrop*'
Inspect files:
IR_DIR=$(find "$RUN" -maxdepth 1 -type d -iname '*irdrop*' | head -1)
find "$IR_DIR" -maxdepth 2 -type f -printf '%p\n' | sort
Search reports:
grep -RniE "drop|voltage|worst|VDD|VPWR|VGND" "$IR_DIR" | head -100
8. Be careful interpreting tiny-design power
Our ALU is extremely small.
Its current profile is not representative of a CPU or large accelerator.
The goal here is to understand:
the analysis
the physical structure
the meaning of voltage drop
not to claim this block has solved full-chip power integrity.
9. Save screenshots
Save:
screenshots/pdn.png
screenshots/ir_drop.png
If the GUI provides an IR-drop heatmap for the loaded state, use it.
Results
Record:
| PDN item | observation |
|---|---|
| Main visible power layers | |
| Standard-cell rail direction | |
| Strap direction | |
| Worst reported voltage drop | |
| Any PDN warnings |
Checklist
- Can identify VDD/ground structures
- Know what a via does
- Know why PDN is not ordinary signal routing
- Understand IR drop
- Found the IR-drop report
- Saved PDN evidence
- Continued to page 17
Questions? Ask in the network Discord.
Placement: where the standard cells physically go
Placement assigns physical coordinates to the synthesized standard cells while trying to satisfy wirelength, density, routability, and timing goals.
Overview
Before placement, the netlist says:
cell A connects to cell B
cell B connects to cell C
but it does not say:
cell A is at x=43.2 µm, y=71.4 µm
Placement creates those coordinates.
Global placement
Global placement finds good approximate locations.
Cells may not yet be perfectly legal on placement sites.
It tries to optimize objectives such as:
wirelength
density
routability
sometimes timing
Detailed placement
Detailed placement legalizes cells:
inside legal rows
on legal sites
without illegal overlaps
while trying not to destroy the global-placement solution.
Why placement affects timing
Wire delay depends on physical distance and load.
A net that was "just a wire" in RTL may become:
a long RC interconnect
in silicon.
Moving connected cells closer can reduce wire delay and capacitance.
Why placement affects routing
If too many nets need the same region:
routing demand > routing capacity
and congestion appears.
Prerequisites
Steps
1. Find global placement
RUN=$(ls -dt runs/* | head -1)
GPL_DIR=$(find "$RUN" -maxdepth 1 -type d \
-name '*openroad-globalplacement' | tail -1)
echo "$GPL_DIR"
There may be an earlier globalplacementskipio stage.
For this page use the later main global-placement state when available.
2. Open global placement in OpenROAD
librelane \
--with-initial-state "$GPL_DIR/state_out.json" \
--flow OpenInOpenROAD \
"$RUN/resolved.json"
3. Identify cell clusters
Zoom in.
Try to distinguish:
flip-flops
combinational cells
buffers
physical-only cells
Use instance selection/properties in the GUI.
4. Find detailed placement
DPL_DIR=$(find "$RUN" -maxdepth 1 -type d \
-name '*detailedplacement*' | head -1)
echo "$DPL_DIR"
Open it:
librelane \
--with-initial-state "$DPL_DIR/state_out.json" \
--flow OpenInOpenROAD \
"$RUN/resolved.json"
5. Compare global vs detailed placement
Look for:
- cells snapped to rows
- overlap removal
- local movement
- row alignment
6. Open heatmaps
In OpenROAD GUI, inspect available heatmaps such as:
placement density
routing congestion
power/IR-drop views if available for the state
A heatmap converts a huge physical dataset into spatial information.
For congestion:
hot region
= lots of routing demand relative to capacity
7. Understand density
If placement is too dense:
cells compete for routing access
buffers have nowhere to go
routing detours increase
timing can worsen
If placement is too sparse:
die/core becomes larger
wirelength can grow
area efficiency decreases
The goal is not maximum density.
The goal is a design that closes:
timing
routing
power
physical verification
at acceptable area.
8. Inspect optimization cells
Physical design may insert or resize cells that were not obvious in the original RTL:
buffers
stronger/slower drive variants
timing repair cells
This is normal.
The physical netlist evolves during implementation.
9. Save screenshots
Save:
screenshots/global_placement.png
screenshots/detailed_placement.png
screenshots/congestion_heatmap.png
Results
Record:
| placement item | observation |
|---|---|
| Placement density | |
| Visible whitespace | |
| Congestion hotspots | |
| Added buffers visible | |
| Legalized rows | yes/no |
Checklist
- Understand global placement
- Understand detailed placement
- Can see legal standard-cell rows
- Can explain why density is not simply "higher is better"
- Inspected congestion
- Saved placement screenshots
- Continued to page 18
Questions? Ask in the network Discord.
Clock tree synthesis: build the physical clock network
Clock Tree Synthesis, or CTS, turns the logical clock connection into a physical network designed to reach every sequential element with controlled delay and skew.
Overview
Before CTS, the netlist conceptually says:
clk → every flip-flop
A single ideal driver cannot physically drive a large chip.
Real clock routing has:
wire resistance
wire capacitance
sink capacitance
buffer delay
different path lengths
CTS inserts a tree of clock buffers.
Conceptually:
clk
|
clock buffer
/ \
buffer buffer
/ \ / \
FF FF FF FF
Clock latency
Clock latency is the time required for the clock edge to travel from its source to a sink.
Clock skew
Clock skew is the difference in clock arrival time between sinks.
If:
FF_A clock arrives at 1.0 ns
FF_B clock arrives at 1.2 ns
then the difference is:
0.2 ns
Skew affects setup and hold timing.
Why clocks receive special treatment
The clock controls when state changes.
Clock quality affects essentially every register-to-register timing path in the block.
Prerequisites
Steps
1. Find the CTS step
RUN=$(ls -dt runs/* | head -1)
CTS_DIR=$(find "$RUN" -maxdepth 1 -type d \
-name '*openroad-cts*' | head -1)
echo "$CTS_DIR"
2. Open the post-CTS design
librelane \
--with-initial-state "$CTS_DIR/state_out.json" \
--flow OpenInOpenROAD \
"$RUN/resolved.json"
3. Highlight the clock net
Use OpenROAD's GUI to select or search for:
clk
Trace the network.
Look for clock buffers added by CTS.
4. Compare pre-CTS and post-CTS cell counts
Compare an earlier placement state with the CTS state.
CTS normally increases the number of cells because it inserts clock-tree elements.
5. Find clock reports
Search the CTS and later STA directories:
grep -RniE "skew|latency|clock" "$CTS_DIR" | head -100
Later multi-corner STA reports may provide more complete clock information.
6. Understand insertion delay
The clock at a register is not an ideal zero-delay event.
The tree contributes real delay.
This is why post-CTS timing is more physically realistic than pre-CTS timing.
7. Setup intuition
For a simplified register-to-register path:
launch FF
→ combinational logic
→ capture FF
the data must arrive early enough before the capture clock edge.
A setup violation means the data path is too slow for the required cycle after accounting for clock timing and constraints.
8. Hold intuition
Data must not change too soon after the capture clock edge.
A hold violation is dangerous because simply lowering clock frequency does not automatically fix it.
Hold is about minimum delay, not maximum cycle time.
9. Save evidence
Save:
screenshots/clock_tree.png
Try to make the highlighted clock network visible.
Results
Record:
| CTS item | value/observation |
|---|---|
| Clock period | |
| Clock buffers inserted | |
| Reported skew | |
| Clock latency/insertion delay | |
| Post-CTS setup status | |
| Post-CTS hold status |
Checklist
- Understand CTS
- Understand clock latency
- Understand clock skew
- Can visually identify the clock network
- Understand setup vs hold at a high level
- Saved clock-tree screenshot
- Continued to page 19
Questions? Ask in the network Discord.
Routing: turn nets into metal and vias
Routing creates the physical metal shapes that electrically connect the placed cells.
Overview
Placement gives each pin a location.
Routing must connect pins through legal metal tracks while obeying:
minimum width
minimum spacing
via rules
routing-layer rules
obstructions
capacity
antenna constraints
Global routing
Global routing decides approximate paths and routing regions.
Think:
which corridors and layers should this net use?
It estimates congestion and creates routing guides.
Detailed routing
Detailed routing assigns actual legal tracks, segments, and vias.
Think:
exactly which metal shape goes where?
Preferred routing directions
Metal layers often have preferred horizontal or vertical routing directions.
Alternating directions makes multi-layer routing manageable.
Do not assume every wire stays on one layer.
Vias
Whenever a routed connection changes metal layers, a via provides the vertical electrical connection.
Congestion
Congestion occurs when too many nets compete for limited routing resources.
The router may respond by:
- detouring
- changing layers
- moving through less congested regions
Long detours add wirelength and parasitic RC.
Prerequisites
Steps
1. Find global routing
RUN=$(ls -dt runs/* | head -1)
GRT_DIR=$(find "$RUN" -maxdepth 1 -type d \
-name '*globalrouting*' | head -1)
echo "$GRT_DIR"
2. Open global-routing state
librelane \
--with-initial-state "$GRT_DIR/state_out.json" \
--flow OpenInOpenROAD \
"$RUN/resolved.json"
Inspect congestion/route guides if available in the GUI.
3. Find detailed routing
DRT_DIR=$(find "$RUN" -maxdepth 1 -type d \
-name '*detailedrouting*' | head -1)
echo "$DRT_DIR"
Open it:
librelane \
--with-initial-state "$DRT_DIR/state_out.json" \
--flow OpenInOpenROAD \
"$RUN/resolved.json"
4. Toggle metal layers
Turn layers on and off individually.
Observe that:
- one layer tends to carry one direction
- another carries the other direction
- vias connect them
5. Follow one signal
Select a net such as one output bit or an internal arithmetic net.
Trace it across:
driver pin
wire segment
via
another metal layer
sink pin
6. Inspect the clock route
Compare the clock network with ordinary signal nets.
The clock may use different routing behavior or layer constraints than general signals.
7. Check total wirelength
The Classic flow includes wirelength reporting.
Search:
find "$RUN" -maxdepth 1 -type d -iname '*wirelength*'
and:
grep -RniE "wire|length" "$RUN"/*wirelength* 2>/dev/null | head -80
8. Understand antenna violations
During fabrication, long unconnected metal structures can accumulate charge during plasma processing.
That charge can damage thin transistor gate oxides.
This is the antenna effect.
The flow checks antenna properties and may repair violations using methods such as:
- routing changes
- antenna diodes
Antenna rules are manufacturing rules, not logical RTL rules.
9. Inspect detailed-routing DRC
Detailed routing also checks routing-rule violations.
Search:
grep -RniE "violation|drc|error" "$DRT_DIR" | head -100
10. Save screenshots
Save:
screenshots/global_routing.png
screenshots/detailed_routing.png
screenshots/metal_layers.png
Results
Record:
| routing item | value/observation |
|---|---|
| Total wirelength | |
| Routing congestion | |
| Detailed-route violations | |
| Antenna violations before repair | |
| Antenna violations after repair | |
| Lowest signal routing layer | |
| Highest signal routing layer |
Checklist
- Understand global routing
- Understand detailed routing
- Can identify metal wires
- Can identify vias
- Inspected multiple metal layers
- Understand congestion
- Understand antenna effect
- Continued to page 20
Questions? Ask in the network Discord.
Parasitics and static timing analysis
After routing, the wires are no longer abstract connections. Their physical geometry creates resistance and capacitance that must be extracted and included in timing analysis.
Overview
An RTL wire looks ideal:
wire carry;
A silicon interconnect behaves more like a distributed electrical network containing:
resistance
capacitance
coupling
driver resistance
receiver capacitance
Longer wires generally mean more parasitic effects.
RC extraction
OpenRCX extracts physical interconnect parasitics from the routed design.
These may be written into:
SPEF
files.
SPEF stands for Standard Parasitic Exchange Format.
Static Timing Analysis
STA analyzes timing mathematically without applying every possible simulation vector.
For each timing path it uses:
cell delays
wire parasitics
clock timing
constraints
slew
load
PVT corner
to calculate arrival and required times.
Slack
Simplified:
slack = required arrival time - actual arrival time
For setup:
positive slack = passes
zero slack = exactly at limit
negative slack = violation
Prerequisites
Steps
1. Find RC extraction
RUN=$(ls -dt runs/* | head -1)
RCX_DIR=$(find "$RUN" -maxdepth 1 -type d \
-name '*openroad-rcx*' | head -1)
echo "$RCX_DIR"
Inspect output files:
find "$RCX_DIR" -maxdepth 2 -type f -printf '%p\n' | sort
2. Find SPEF
find "$RUN" -type f -iname '*.spef' | head -20
Do not try to manually understand an entire SPEF file.
Open the beginning:
SPEF=$(find "$RUN" -type f -iname '*.spef' | head -1)
head -80 "$SPEF"
Notice:
nets
capacitances
resistances
connections
3. Find post-PNR STA
STA_DIR=$(find "$RUN" -maxdepth 1 -type d \
-name '*stapostpnr*' | head -1)
echo "$STA_DIR"
List reports:
find "$STA_DIR" -type f -printf '%p\n' | sort
4. Open the timing summary
Look for:
summary.rpt
For example:
cat "$STA_DIR/summary.rpt"
Record:
- worst setup slack
- worst hold slack
- total negative setup slack
- total negative hold slack if reported
- violating endpoints
- corner
5. Inspect the worst setup path
Search the corner reports:
grep -Rni "slack" "$STA_DIR" | head -100
Open the report containing the worst max/setup timing path.
Identify:
launch register
logic cells
net delays
capture register
clock arrival information
final slack
6. Inspect the worst hold path
Find the min/hold report.
Notice that the worst hold path may be very short.
That is expected.
Hold analysis asks whether data changes too early.
7. Understand cell delay vs net delay
A timing path is not just logic gates.
It contains:
cell delay + interconnect delay
After route, net delay can become a major part of the critical path.
This is one reason post-route STA is more meaningful than an RTL-level timing guess.
8. Understand timing corners
A design should be checked across the PVT/interconnect corners configured by the PDK.
Different corners can dominate:
setup
hold
slew
power
Do not assume the same corner is worst for everything.
9. Open timing paths graphically
Open final OpenROAD:
librelane --last-run --flow OpenInOpenROAD config.json
Use the timing-path viewer to inspect the worst path spatially.
Ask:
Is it physically long?
Does it cross the ALU?
Is most delay cell delay or net delay?
Does it contain buffering?
10. Save evidence
Save:
screenshots/worst_setup_path.png
screenshots/worst_hold_path.png
Results
Record:
| timing metric | result |
|---|---|
| Clock period | |
| Worst setup slack | |
| Total negative setup slack | |
| Worst hold slack | |
| Setup-violating endpoints | |
| Hold-violating endpoints | |
| Worst setup corner | |
| Worst hold corner | |
| Critical path startpoint | |
| Critical path endpoint |
Checklist
- Found SPEF
- Understand resistance and capacitance parasitics
- Found post-route STA
- Recorded worst setup slack
- Recorded worst hold slack
- Inspected the worst timing path
- Understand cell delay vs net delay
- Understand multi-corner analysis
- Continued to page 21
Questions? Ask in the network Discord.
Power, performance, area, and efficiency
A physical design is not judged by one number. Power, performance, area, routability, and correctness interact, and improving one can make another worse.
Overview
The common shorthand is:
PPA
meaning:
Power
Performance
Area
Power
Digital power is often discussed as:
dynamic power
static/leakage power
A common intuition for switching power is:
P_dynamic ∝ α C V² f
where:
αis switching activityCis capacitanceVis supply voltagefis switching frequency
This is an intuition, not a complete chip-power equation.
Performance
For this synchronous block, performance is constrained by timing.
A smaller achievable clock period means a higher possible clock frequency:
f = 1 / T
Area
ASIC area may refer to different things:
standard-cell area
core area
die/block area
Do not mix them.
Efficiency is not one universal metric
Examples include:
area efficiency
energy per operation
performance per area
power density
Choose a metric that matches the design goal.
Prerequisites
Steps
1. Open final metrics
RUN=$(ls -dt runs/* | head -1)
cat "$RUN/final/metrics.csv"
For machine-readable inspection:
python3 -m json.tool "$RUN/final/metrics.json" | less
2. Search useful metrics
grep -iE \
'area|util|power|slack|tns|wns|wire|cell|drc|lvs|antenna|ir' \
"$RUN/final/metrics.csv"
Metric names can change as the flow evolves.
Use the semantic meaning, not a memorized line number.
3. Record physical area
Record separately:
standard-cell area
core area
die/block area
For our explicit floorplan:
die = 150 µm × 150 µm
but your actual reports are the authority.
4. Calculate area utilization
A useful conceptual metric is:
cell utilization =
standard-cell area / core area
Do not confuse this with placement target density.
5. Record performance
Use post-route timing.
If your clock period is:
10 ns
the requested frequency is:
100 MHz
But if setup timing fails, the design has not demonstrated operation at that target.
A rough period estimate based only on worst setup slack can help intuition:
approximate required period
≈ constrained period - worst setup slack
when WNS is negative under the same assumptions.
Treat this as an approximation, not a complete signoff calculation.
6. Inspect power reports
Find power reports:
find "$RUN" -type f -iname '*power*' -o -iname '*irdrop*'
Search:
grep -RniE "total|internal|switch|leak|power" \
"$RUN"/*sta* "$RUN"/*power* 2>/dev/null | head -120
Record the categories that the current flow actually reports.
7. Interpret power carefully
Power accuracy depends on switching-activity assumptions.
If the flow does not have realistic activity information, treat power as an estimate suitable for learning and relative comparison.
Do not present a vectorless estimate as measured silicon power.
8. Define one project efficiency metric
For example:
performance density =
frequency target / core area
or:
cell packing efficiency =
standard-cell area / core area
State exactly what you calculated and its units.
Do not invent a vague single "chip efficiency percentage."
9. Think in tradeoffs
Examples:
smaller core
→ less area
→ possibly more congestion
→ potentially worse timing
larger drive cells
→ potentially better timing
→ more area
→ more load/power
more buffers
→ may fix slew/timing
→ adds area and power
lower clock frequency
→ easier setup timing
→ lower throughput
10. Save your PPA table
Add to the project README:
## ASIC PPA
| metric | value |
|--------|------:|
| Standard-cell area | |
| Core area | |
| Die/block area | |
| Cell utilization | |
| Clock period | |
| Requested frequency | |
| Worst setup slack | |
| Worst hold slack | |
| Total power estimate | |
| Dynamic/switching power | |
| Leakage/static power | |
| Worst IR drop | |
| Total routed wirelength | |
Results
You should be able to explain why this statement is wrong:
Design A is better because it has fewer cells.
A complete comparison needs the design objective and physical results.
Checklist
- Recorded standard-cell area
- Recorded core and die area
- Calculated utilization
- Recorded timing
- Recorded power estimate
- Recorded IR drop
- Recorded wirelength
- Defined any efficiency metric explicitly
- Continued to page 22
Questions? Ask in the network Discord.
Signoff checks: DRC, LVS, antenna, XOR, and manufacturability
A GDS file is only useful if its geometry is legal and its extracted connectivity matches the intended circuit.
Overview
Several different checks answer different questions.
| check | question |
|---|---|
| DRC | Is the geometry legal for the manufacturing rules? |
| LVS | Does the extracted layout connectivity match the schematic/netlist? |
| Antenna | Could manufacturing charge damage transistor gates? |
| XOR | Do two independently generated layout streams match geometrically? |
| STA | Does timing satisfy the constraints across required corners? |
| IR drop | Is the supply network electrically acceptable under the modeled conditions? |
Passing one does not imply passing the others.
DRC
Design Rule Checking tests geometry rules such as:
minimum metal width
minimum spacing
via enclosure
minimum area
layer interaction rules
LVS
Layout Versus Schematic asks:
Does the circuit extracted from the physical layout
match the intended netlist?
DRC can pass while LVS fails.
A beautiful legal layout can still be electrically wrong.
XOR
LibreLane can stream GDS through more than one backend and compare them.
Geometric differences may reveal stream-out inconsistencies.
Prerequisites
Steps
1. Find DRC steps
RUN=$(ls -dt runs/* | head -1)
find "$RUN" -maxdepth 1 -type d \
\( -iname '*magic-drc*' -o -iname '*klayout-drc*' \)
2. Search DRC reports
grep -RniE "drc|violation|error|count" \
"$RUN"/*drc* 2>/dev/null | head -120
Record Magic and KLayout DRC results separately if both are present.
3. Find LVS
LVS_DIR=$(find "$RUN" -maxdepth 1 -type d \
-iname '*netgen-lvs*' | head -1)
echo "$LVS_DIR"
Inspect:
grep -RniE "match|mismatch|lvs|error|net|device" \
"$LVS_DIR" | head -150
The desired result is logical/layout equivalence under the checker's rules.
4. Find antenna checks
find "$RUN" -maxdepth 1 -type d -iname '*antenna*'
Inspect the reports before and after repair when both exist.
5. Find XOR
find "$RUN" -maxdepth 1 -type d -iname '*xor*'
Look for the reported geometric-difference count.
6. Read the manufacturability summary
Find:
find "$RUN" -maxdepth 2 -type f \
-iname '*manufactur*' -o -iname '*summary*'
Also inspect:
error.log
warning.log
from the run root.
7. Do not ignore deferred errors
Some flows continue through multiple checks so you can collect more information before the final failure.
That means:
flow reached GDS
does not necessarily mean:
flow passed signoff
Read the final status and checker reports.
8. Understand what "clean" means
For this educational macro, a strong result is:
0 DRC violations
LVS clean
0 unresolved antenna violations
0 disconnected pins/nets
no setup violations
no hold violations
acceptable IR-drop report
consistent stream-out/XOR result
The exact requirements for a real tapeout are defined by the foundry/shuttle/integration platform.
9. Save evidence
Save:
screenshots/drc_clean.png
screenshots/lvs_clean.png
screenshots/signoff_summary.png
Results
Create:
| signoff item | result |
|---|---|
| Magic DRC | |
| KLayout DRC | |
| LVS | |
| Antenna | |
| XOR | |
| Setup | |
| Hold | |
| Disconnected pins | |
| IR drop |
Checklist
- Checked Magic DRC
- Checked KLayout DRC
- Checked LVS
- Checked antenna
- Checked XOR
- Checked setup
- Checked hold
- Checked run error/warning logs
- Continued to page 23
Questions? Ask in the network Discord.
Inspect every stage and every major ASIC file
The run directory is a history of the chip being constructed. Use intermediate states to visually inspect what changed at each step instead of treating the flow as a black box.
Overview
Each LibreLane step receives a design state and produces a new state.
A typical step directory contains files such as:
COMMANDS
config.json
*.log
*.odb
*.def
*.nl.v
*.pnl.v
*.sdc
state_in.json
state_out.json
Important formats
| format | purpose |
|---|---|
.v RTL | behavioral/source logic |
.nl.v | gate-level netlist without explicit power connections |
.pnl.v | gate-level netlist with power connections |
.sdc | timing constraints |
.def | physical implementation exchange: rows, components, pins, routing |
.lef | abstract macro physical view |
.odb | OpenROAD/OpenDB design database |
.spef | extracted parasitic resistance/capacitance |
.sdf | timing delays for timing-aware simulation |
.lib | Liberty timing/power model |
.spice | electrical/transistor-level netlist |
.gds | final geometric mask-layout stream |
LEF vs GDS
LEF is intentionally abstract.
A macro LEF tells a parent design things such as:
block dimensions
pin shapes
routing obstructions
without requiring the parent router to understand every transistor polygon.
GDS contains the detailed physical geometry.
DEF vs GDS
DEF describes an implementation using design objects and physical coordinates.
GDS is a lower-level geometric layout stream.
ODB
ODB is OpenROAD's database representation.
It is especially useful because OpenROAD can reopen the design with rich knowledge of:
instances
nets
timing
routing
layers
parasitics
Prerequisites
Steps
1. Find the latest run
RUN=$(ls -dt runs/* | head -1)
echo "$RUN"
2. List all step directories
find "$RUN" -maxdepth 1 -mindepth 1 -type d \
-printf '%f\n' | sort
3. Make a stage table
For each of these stages, find the matching directory:
Yosys synthesis
floorplan
tap/endcap insertion
PDN generation
global placement
detailed placement
CTS
global routing
detailed routing
RC extraction
post-PNR STA
GDS stream-out
DRC
LVS
Write the actual folder names into your README.
4. Generic command to open an intermediate OpenROAD state
Suppose:
STEP_DIR="$RUN/<step-folder>"
Then:
librelane \
--with-initial-state "$STEP_DIR/state_out.json" \
--flow OpenInOpenROAD \
"$RUN/resolved.json"
Use this repeatedly.
5. Open detailed routing in KLayout
DRT_DIR=$(find "$RUN" -maxdepth 1 -type d \
-name '*detailedrouting*' | head -1)
librelane \
--with-initial-state "$DRT_DIR/state_out.json" \
--flow OpenInKLayout \
"$RUN/resolved.json"
If a GDS does not exist yet for that state, the viewer can use the physical DEF view.
6. Open final GDS in KLayout
librelane --last-run --flow OpenInKLayout config.json
7. Open final design in OpenROAD
librelane --last-run --flow OpenInOpenROAD config.json
8. Use KLayout layer controls
In KLayout:
- zoom into one standard cell
- toggle metal layers
- toggle via layers
- identify the PDN
- identify signal routes
- locate block pins
- inspect the die boundary
- inspect repeated standard-cell geometry
At very high zoom you are no longer looking at "gates" as schematic symbols.
You are looking at physical layout polygons.
9. Do not confuse colors with electrical meaning
EDA viewers assign display colors to layers.
The color is a visualization choice.
The layer name and datatype define the physical meaning.
10. Inspect final deliverables
List final files:
find "$RUN/final" -type f -printf '%p\n' | sort
Then answer:
Which file would a parent block use for abstract placement?
Which file contains final layout geometry?
Which file contains parasitics?
Which file contains delays?
Which file contains powered connectivity?
Which file contains timing constraints?
11. Build a visual progression
Save one screenshot from each major stage:
01_synthesis_or_netlist.png
02_floorplan.png
03_pdn.png
04_global_placement.png
05_detailed_placement.png
06_cts.png
07_global_routing.png
08_detailed_routing.png
09_final_gds.png
Put them in order.
This progression is one of the best ways to understand RTL-to-GDS.
Results
You should now be able to look at an arbitrary file from a digital ASIC flow and explain why it exists.
Checklist
- Opened multiple intermediate states
- Understand RTL vs gate-level netlist
- Understand powered vs unpowered netlist
- Understand LEF
- Understand DEF
- Understand ODB
- Understand SPEF
- Understand SDF
- Understand Liberty
- Understand SPICE
- Understand GDSII
- Built the visual stage progression
- Continued to page 24
Questions? Ask in the network Discord.
Optimize the design and compare the adder architectures
Now repeat the flow as an engineering experiment. Compare the Ripple Carry, Carry Lookahead, and Carry Select versions using the same process, clock constraint, floorplan methodology, and reporting method.
Overview
A fair comparison changes one primary design variable at a time.
For the adder experiment:
same ALU
same top-level registers
same PDK
same standard-cell library
same clock period
same physical constraints
same tool flow
different adder RTL
Then compare:
area
timing
power estimate
wirelength
cell count
buffer count
routing
congestion
Why FPGA results and ASIC results can disagree
An FPGA has:
LUTs
dedicated carry chains
fixed routing architecture
fixed programmable resources
An ASIC has:
standard cells
custom placement
custom routing
different wire RC
different cell choices
An architecture that performs well on an FPGA is not guaranteed to rank the same way in an ASIC.
Prerequisites
- inspection and file formats
- one complete clean baseline run
Steps
1. Create three design copies
For example:
experiments/
├── ripple/
├── lookahead/
└── carry_select/
Each folder should use the appropriate adder8.v.
2. Keep the baseline configuration identical
Use the same:
CLOCK_PERIOD
PDK
STD_CELL_LIBRARY
DIE_AREA
CORE_AREA
placement density
flow version
for the first comparison.
Do not secretly give one architecture more area.
3. Run all three
Example:
cd experiments/ripple
librelane config.json
cd ../lookahead
librelane config.json
cd ../carry_select
librelane config.json
4. Record synthesis results
For each version, record:
mapped cell count
estimated cell area
major cell types
5. Record physical results
For each version:
standard-cell area
die/core area
utilization
wirelength
buffer count
6. Record timing
For each version:
worst setup slack
worst hold slack
critical path
critical-path corner
7. Record power
Use the same analysis assumptions for all three.
Record the power categories reported by the flow.
Relative comparison is much more meaningful when the methodology is identical.
8. Record signoff quality
Do not rank a design with violations above a clean design just because one PPA number looks attractive.
Record:
DRC
LVS
antenna
setup
hold
routing violations
9. Build the comparison table
| metric | Ripple | Lookahead | Carry Select |
|--------|-------:|----------:|-------------:|
| Mapped cells | | | |
| Standard-cell area | | | |
| Core area | | | |
| Utilization | | | |
| Wirelength | | | |
| Worst setup slack | | | |
| Worst hold slack | | | |
| Power estimate | | | |
| Worst IR drop | | | |
| DRC clean | | | |
| LVS clean | | | |
10. Explain the result physically
Do not stop at:
Lookahead was faster.
Explain:
what did synthesis map?
what is the critical path?
how many cells were inserted?
what happened to wirelength?
did routing become harder?
did the architecture survive optimization?
11. Experiment with utilization
After the fair baseline comparison, pick one adder and vary floorplan size or utilization methodology.
For example, compare:
roomy core
moderate core
tight core
Do not choose values that make the design trivially impossible.
Observe:
cell density
congestion
wirelength
timing
area
12. Experiment with clock period
Try a more aggressive clock target.
Example progression:
10 ns
8 ns
6 ns
Do not assume the tool will close timing.
The point is to find where implementation becomes difficult and understand why.
13. Optional synthesis exploration
LibreLane provides a synthesis-exploration flow that can try multiple synthesis strategies.
Use:
librelane --flow SynthesisExploration config.json
Treat this as an optimization experiment after you understand the baseline.
14. Keep reproducibility information
Save:
resolved.json
metrics.csv
metrics.json
tool version
PDK version
for each run.
If another student cannot reproduce your comparison, it is not a strong engineering comparison.
Results
Your conclusion should answer:
- Which adder used the least cell area?
- Which had the best setup timing?
- Which had the most routing/wirelength?
- Which used the most estimated power?
- Did the original RTL structure remain recognizable?
- Did the ranking match your Vivado/FPGA result?
- What tradeoff would you choose for a real design?
Checklist
- Ran all three adder architectures
- Used identical baseline physical constraints
- Compared cell area
- Compared timing
- Compared power
- Compared wirelength
- Compared signoff status
- Performed one density/area experiment
- Performed one clock-period experiment
- Wrote a physical explanation, not only a ranking
- Continued to page 25
Questions? Ask in the network Discord.
Final RTL-to-GDS submission and tapeout-readiness checklist
Package the complete project so another engineer can reproduce the design, inspect every major stage, verify the signoff results, and understand what remains before actual fabrication.
Overview
A successful tutorial result is not just:
alu_top.gds
A strong ASIC deliverable includes:
source
constraints
configuration
run metadata
physical views
timing data
parasitics
verification reports
PPA metrics
engineering interpretation
Hardened macro vs full chip
Your alu_top is a hardened digital block.
It is not automatically a full standalone chip with:
pad ring
ESD
package
power pads
clock pad
reset pad
bond wires
bumps
shuttle wrapper
foundry delivery package
A higher-level design can integrate the macro using abstract views such as LEF plus timing models and connect it into a full-chip implementation.
"GDS exists" is not signoff
Before a fabrication submission, the relevant project must satisfy the actual foundry/shuttle signoff requirements.
For this class, you will demonstrate the corresponding open-source checks and clearly state their status.
Prerequisites
Steps
1. Final repository structure
A good repository looks like:
asic_101_gds/
├── rtl/
│ ├── adder8.v
│ ├── alu_core.v
│ └── alu_top.v
├── screenshots/
│ ├── baseline_gds.png
│ ├── synthesis_cells.png
│ ├── floorplan.png
│ ├── tap_endcap.png
│ ├── pdn.png
│ ├── global_placement.png
│ ├── detailed_placement.png
│ ├── congestion_heatmap.png
│ ├── clock_tree.png
│ ├── global_routing.png
│ ├── detailed_routing.png
│ ├── metal_layers.png
│ ├── worst_setup_path.png
│ ├── worst_hold_path.png
│ ├── drc_clean.png
│ └── lvs_clean.png
├── config.json
└── README.md
Do not commit giant disposable run directories unless your team specifically wants them in Git.
Instead, archive or publish the required final reports/views according to the network submission instructions.
2. Record reproducibility metadata
In README:
librelane --version
yosys -V
openroad -version
Also record:
PDK
standard-cell library
clock period
adder architecture
date of run
Keep the run's:
resolved.json
with your archived submission when requested.
3. Final PPA table
| metric | value |
|--------|------:|
| Adder architecture | |
| PDK | sky130A |
| Standard-cell library | sky130_fd_sc_hd |
| Clock period | |
| Requested frequency | |
| Mapped cells | |
| Standard-cell area | |
| Core dimensions | |
| Core area | |
| Die dimensions | |
| Die area | |
| Utilization | |
| Total routed wirelength | |
| Worst setup slack | |
| Worst hold slack | |
| Total negative setup slack | |
| Power estimate | |
| Worst IR drop | |
4. Final signoff table
| check | status |
|-------|--------|
| RTL simulation | |
| Lint | |
| Synthesis | |
| Detailed routing | |
| Routing DRC | |
| Antenna | |
| Magic DRC | |
| KLayout DRC | |
| LVS | |
| Setup timing | |
| Hold timing | |
| XOR/stream comparison | |
| IR-drop report | |
Never write PASS unless you actually inspected the report.
5. Include the stage progression
Your README should show or link to:
floorplan
→ PDN
→ placement
→ CTS
→ routing
→ final GDS
For each image, write one sentence explaining what physically changed.
6. Explain the critical path
Include:
startpoint
endpoint
clock corner
logic/network path
worst slack
Then explain whether the delay is dominated by:
logic depth
cell delay
wire delay
routing distance
fanout
7. Explain your area
Distinguish:
cell area
core area
die area
and explain why they are different.
8. Explain your power result
State:
what report produced it
what switching/activity assumptions were available
whether the value is measured or estimated
For this project it is an estimate.
9. Explain what GDS contains
Your final GDS contains physical geometric structures for the hardened block.
You should be able to point out:
- die/block boundary
- standard-cell rows
- standard cells
- PDN
- clock network
- signal routing
- vias
- filler structures
- pins
10. Explain what still remains for a real chip
A real product/shuttle integration may still require:
- top-level SoC integration
- hardened memory/analog macros
- padframe
- IO cells
- ESD
- package/bump/bond planning
- full-chip power integrity
- full-chip clock/reset planning
- shuttle wrapper
- density/fill requirements
- foundry-specific signoff decks
- final tapeout package review
The exact checklist depends on the fabrication program.
11. Write the final engineering conclusion
Answer:
- What did you build?
- Which adder did you choose?
- What happened to it during synthesis?
- What limited performance?
- What determined area?
- Where did routing consume space?
- Did the design pass DRC and LVS?
- Did setup and hold timing pass?
- What would you optimize next?
- What is the next step between this macro and manufactured silicon?
12. Final concept check
You should be able to explain the entire sentence below without hand-waving:
The RTL was synthesized by Yosys into Sky130 standard cells, floorplanned and placed into a die/core geometry by OpenROAD, connected to a generated PDN, clock-buffered during CTS, routed with legal metal and vias, extracted into parasitic RC, analyzed with multi-corner STA and power/IR-drop estimation, physically checked with DRC/antenna/LVS-related signoff steps, and streamed into GDSII for physical integration or fabrication preparation.
Results
You have completed an end-to-end introductory digital ASIC flow:
RTL
→ GATES
→ FLOORPLAN
→ PDN
→ PLACEMENT
→ CTS
→ ROUTING
→ EXTRACTION
→ STA
→ POWER
→ DRC/LVS
→ GDSII
The important result is not the colorful GDS screenshot.
The important result is that you understand what changed at each stage, what each artifact means, and what evidence is required before claiming the design is physically correct.
Checklist
- Repository is organized
- Tool and PDK information recorded
- PPA table complete
- Signoff table complete
- Stage screenshots complete
- Critical path explained
- Area explained
- Power estimate explained
- DRC checked
- LVS checked
- Setup checked
- Hold checked
- Final GDS inspected
- Hardened macro vs complete chip distinction understood
- Final engineering conclusion written
- ASIC 101 RTL-to-GDS complete
Questions? Ask in the network Discord.
RTL
This section is still being written. Check back soon — in the meantime, the Quick Start Tutorial covers RTL design as part of building the 8-bit ALU.
Simulation
This section is still being written. Check back soon — in the meantime, the Quick Start Tutorial covers verifying a design in simulation.
Place & Route
This section is still being written. Check back soon — in the meantime, the Quick Start Tutorial walks through the open-source place-and-route flow.
Additional Projects
Smaller, self-contained builds that don't fit the main course sequence. More are on the way — for now, see Ring Oscillator.
Ring Oscillator
A 5-stage ring oscillator on SKY130, from schematic to simulated frequency. This page doubles as the formatting reference: every construct the renderer understands is used somewhere below.
TL;DR: an odd number of inverters in a loop has no stable DC operating point, so it oscillates at roughly
f = 1 / (2 * N * t_pd). Five stages of the standard inverter lands near 1.2 GHz at tt.
Overview
The ring oscillator is the hello world of analog design: it needs correct device models, a working simulator, and nothing else. If this page simulates cleanly, your toolchain setup is good and every later tutorial will run.
Inline styles for reference: bold, italic, both, inline code, struck, and an external link to the SKY130 PDK docs.
Prerequisites
xschemandngspiceinstalled and on$PATH- SKY130A PDK with
$PDK_ROOTexported- built with
open_pdks - includes the
sky130_fd_prprimitives library
- built with
- Basic familiarity with SPICE netlists
Schematic

Keep the loop symmetric: identical W/L on every stage, and buffer the tap so the probe capacitance does not load the ring.
Netlist
* ring_osc.spice : 5-stage ring oscillator, sky130 tt
.lib $PDK_ROOT/sky130A/libs.tech/ngspice/sky130.lib.spice tt
.subckt inv in out vdd gnd
XM1 out in gnd gnd sky130_fd_pr__nfet_01v8 W=1 L=0.15
XM2 out in vdd vdd sky130_fd_pr__pfet_01v8 W=2.1 L=0.15
.ends
Xi1 n5 n1 vdd 0 inv
Xi2 n1 n2 vdd 0 inv
Xi3 n2 n3 vdd 0 inv
Xi4 n3 n4 vdd 0 inv
Xi5 n4 n5 vdd 0 inv
Vdd vdd 0 1.8
.ic v(n1)=0
.tran 10p 20n
.end
Run it
- Export the environment
export PDK_ROOT=/usr/local/share/pdk
- Launch the simulation
ngspice ring_osc.spice- wait for the transient to finish
- Measure the period on
v(n5)and invert it
Results
| Corner | VDD | f_osc | Power |
|---|---|---|---|
| tt | 1.80 V | 1.21 GHz | 84 uW |
| ss | 1.62 V | 0.89 GHz | 61 uW |
| ff | 1.98 V | 1.63 GHz | 118 uW |
Frequency tracks 1/t_pd, so expect roughly linear movement with VDD across corners.
Checklist
- schematic drawn, ERC clean
- transient runs at tt
- ss / ff corners swept
- layout drawn, DRC and LVS clean
Further reading
Appendix
Reference material and setup notes will go here. This section is still being written — check back soon.