91 lines
3.2 KiB
Python
91 lines
3.2 KiB
Python
|
|
#!/usr/bin/env python3
|
||
|
|
"""
|
||
|
|
Programmatic synthetic IC layout generator using gdstk.
|
||
|
|
Generates GDS files with simple standard-cell-like patterns, wires, and vias.
|
||
|
|
"""
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import argparse
|
||
|
|
from pathlib import Path
|
||
|
|
import random
|
||
|
|
|
||
|
|
import gdstk
|
||
|
|
|
||
|
|
|
||
|
|
def build_standard_cell(cell_name: str, rng: random.Random, layer: int = 1, datatype: int = 0) -> gdstk.Cell:
|
||
|
|
cell = gdstk.Cell(cell_name)
|
||
|
|
# Basic cell body
|
||
|
|
w = rng.uniform(0.8, 2.0)
|
||
|
|
h = rng.uniform(1.6, 4.0)
|
||
|
|
rect = gdstk.rectangle((0, 0), (w, h), layer=layer, datatype=datatype)
|
||
|
|
cell.add(rect)
|
||
|
|
# Poly fingers
|
||
|
|
nf = rng.randint(1, 4)
|
||
|
|
pitch = w / (nf + 1)
|
||
|
|
for i in range(1, nf + 1):
|
||
|
|
x = i * pitch
|
||
|
|
poly = gdstk.rectangle((x - 0.05, 0), (x + 0.05, h), layer=layer + 1, datatype=datatype)
|
||
|
|
cell.add(poly)
|
||
|
|
# Contact/vias
|
||
|
|
for i in range(rng.randint(2, 6)):
|
||
|
|
vx = rng.uniform(0.1, w - 0.1)
|
||
|
|
vy = rng.uniform(0.1, h - 0.1)
|
||
|
|
via = gdstk.rectangle((vx - 0.05, vy - 0.05), (vx + 0.05, vy + 0.05), layer=layer + 2, datatype=datatype)
|
||
|
|
cell.add(via)
|
||
|
|
return cell
|
||
|
|
|
||
|
|
|
||
|
|
def generate_layout(out_path: Path, width: float, height: float, seed: int, rows: int, cols: int, density: float):
|
||
|
|
rng = random.Random(seed)
|
||
|
|
lib = gdstk.Library()
|
||
|
|
top = gdstk.Cell("TOP")
|
||
|
|
|
||
|
|
# Create a few standard cell variants
|
||
|
|
variants = [build_standard_cell(f"SC_{i}", rng, layer=1) for i in range(4)]
|
||
|
|
|
||
|
|
# Place instances in a grid with random skips based on density
|
||
|
|
x_pitch = width / cols
|
||
|
|
y_pitch = height / rows
|
||
|
|
for r in range(rows):
|
||
|
|
for c in range(cols):
|
||
|
|
if rng.random() > density:
|
||
|
|
continue
|
||
|
|
cell = rng.choice(variants)
|
||
|
|
dx = c * x_pitch + rng.uniform(0.0, 0.1 * x_pitch)
|
||
|
|
dy = r * y_pitch + rng.uniform(0.0, 0.1 * y_pitch)
|
||
|
|
ref = gdstk.Reference(cell, (dx, dy))
|
||
|
|
top.add(ref)
|
||
|
|
|
||
|
|
lib.add(*variants)
|
||
|
|
lib.add(top)
|
||
|
|
lib.write_gds(str(out_path))
|
||
|
|
|
||
|
|
|
||
|
|
def main():
|
||
|
|
parser = argparse.ArgumentParser(description="Generate synthetic IC layouts (GDS)")
|
||
|
|
parser.add_argument("--out-dir", type=str, default="data/synthetic/gds")
|
||
|
|
parser.add_argument("--out_dir", dest="out_dir", type=str, help="Alias of --out-dir")
|
||
|
|
parser.add_argument("--num-samples", type=int, default=10)
|
||
|
|
parser.add_argument("--num", dest="num_samples", type=int, help="Alias of --num-samples")
|
||
|
|
parser.add_argument("--seed", type=int, default=42)
|
||
|
|
parser.add_argument("--width", type=float, default=200.0)
|
||
|
|
parser.add_argument("--height", type=float, default=200.0)
|
||
|
|
parser.add_argument("--rows", type=int, default=10)
|
||
|
|
parser.add_argument("--cols", type=int, default=10)
|
||
|
|
parser.add_argument("--density", type=float, default=0.5)
|
||
|
|
|
||
|
|
args = parser.parse_args()
|
||
|
|
out_dir = Path(args.out_dir)
|
||
|
|
out_dir.mkdir(parents=True, exist_ok=True)
|
||
|
|
|
||
|
|
rng = random.Random(args.seed)
|
||
|
|
for i in range(args.num_samples):
|
||
|
|
sample_seed = rng.randint(0, 2**31 - 1)
|
||
|
|
out_path = out_dir / f"chip_{i:06d}.gds"
|
||
|
|
generate_layout(out_path, args.width, args.height, sample_seed, args.rows, args.cols, args.density)
|
||
|
|
print(f"[OK] Generated {out_path}")
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
main()
|