Files
RoRD-Layout-Recognation/tools/generate_synthetic_layouts.py

91 lines
3.2 KiB
Python
Raw Normal View History

#!/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()