klayout-draw-mcp 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,63 @@
1
+ name: docs
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ paths:
7
+ - "docs/**"
8
+ - "examples/**"
9
+ - "scripts/render_examples.py"
10
+ - "mkdocs.yml"
11
+ - ".github/workflows/docs.yml"
12
+ workflow_dispatch:
13
+
14
+ permissions:
15
+ contents: read
16
+ pages: write
17
+ id-token: write
18
+
19
+ concurrency:
20
+ group: pages
21
+ cancel-in-progress: false
22
+
23
+ jobs:
24
+ build:
25
+ runs-on: ubuntu-latest
26
+ env:
27
+ QT_QPA_PLATFORM: offscreen
28
+ steps:
29
+ - uses: actions/checkout@v4
30
+
31
+ - uses: actions/setup-python@v5
32
+ with:
33
+ python-version: "3.12"
34
+
35
+ # System libraries the headless klayout.lay renderer needs on Linux.
36
+ - name: Install system dependencies
37
+ run: |
38
+ sudo apt-get update
39
+ sudo apt-get install -y --no-install-recommends \
40
+ xvfb libgl1 libegl1 libxkbcommon0 libfontconfig1 libdbus-1-3
41
+
42
+ - name: Install Python dependencies
43
+ run: pip install klayout "mkdocs-material>=9.5"
44
+
45
+ - name: Render example screenshots
46
+ run: xvfb-run -a python scripts/render_examples.py
47
+
48
+ - name: Build site
49
+ run: mkdocs build --strict
50
+
51
+ - uses: actions/upload-pages-artifact@v3
52
+ with:
53
+ path: site
54
+
55
+ deploy:
56
+ needs: build
57
+ runs-on: ubuntu-latest
58
+ environment:
59
+ name: github-pages
60
+ url: ${{ steps.deployment.outputs.page_url }}
61
+ steps:
62
+ - id: deployment
63
+ uses: actions/deploy-pages@v4
@@ -0,0 +1,53 @@
1
+ name: publish
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+ workflow_dispatch:
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ build:
13
+ runs-on: ubuntu-latest
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+
17
+ - uses: actions/setup-python@v5
18
+ with:
19
+ python-version: "3.12"
20
+
21
+ - name: Build sdist and wheel
22
+ run: |
23
+ pip install build
24
+ python -m build
25
+
26
+ - name: Check metadata
27
+ run: |
28
+ pip install twine
29
+ twine check dist/*
30
+
31
+ - uses: actions/upload-artifact@v4
32
+ with:
33
+ name: dist
34
+ path: dist/
35
+
36
+ publish:
37
+ needs: build
38
+ runs-on: ubuntu-latest
39
+ # Only publish on a real GitHub release, not on manual dispatch.
40
+ if: github.event_name == 'release'
41
+ environment:
42
+ name: pypi
43
+ url: https://pypi.org/project/klayout-draw-mcp/
44
+ permissions:
45
+ id-token: write # OIDC token for PyPI Trusted Publishing
46
+ steps:
47
+ - uses: actions/download-artifact@v4
48
+ with:
49
+ name: dist
50
+ path: dist/
51
+
52
+ - name: Publish to PyPI
53
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,20 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ build/
6
+ dist/
7
+ .venv/
8
+
9
+ # Editor / OS
10
+ .vscode/
11
+ .idea/
12
+ .DS_Store
13
+
14
+ # Generated layouts
15
+ *.gds
16
+ *.oas
17
+ *.gds.gz
18
+
19
+ # MkDocs build output
20
+ site/
@@ -0,0 +1 @@
1
+ 3.13
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 geniuskey
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,100 @@
1
+ Metadata-Version: 2.4
2
+ Name: klayout-draw-mcp
3
+ Version: 0.1.0
4
+ Summary: MCP server for drawing, generating, and editing GDS layouts with KLayout.
5
+ Project-URL: Homepage, https://github.com/geniuskey/klayout-draw-mcp
6
+ Project-URL: Documentation, https://geniuskey.github.io/klayout-draw-mcp/
7
+ Project-URL: Repository, https://github.com/geniuskey/klayout-draw-mcp
8
+ Project-URL: Issues, https://github.com/geniuskey/klayout-draw-mcp/issues
9
+ Author-email: geniuskey <geniuskey@gmail.com>
10
+ License: MIT
11
+ License-File: LICENSE
12
+ Keywords: drawing,eda,gds,klayout,layout,mcp
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)
23
+ Requires-Python: >=3.10
24
+ Requires-Dist: klayout>=0.29
25
+ Requires-Dist: mcp>=1.2.0
26
+ Provides-Extra: docs
27
+ Requires-Dist: mkdocs-material>=9.5; extra == 'docs'
28
+ Description-Content-Type: text/markdown
29
+
30
+ # klayout-draw-mcp
31
+
32
+ An [MCP](https://modelcontextprotocol.io) server for **drawing GDS layouts** with **KLayout** — generate, edit, and view layouts from an AI assistant such as Claude Code or Claude Desktop.
33
+
34
+ Layout geometry is built in-process with the standalone [`klayout`](https://pypi.org/project/klayout/) Python module (no GUI needed). The installed KLayout application is launched only for viewing or manual editing.
35
+
36
+ ## Tools
37
+
38
+ | Tool | Purpose |
39
+ | --- | --- |
40
+ | `new_layout(top_cell, dbu)` | Start a new in-memory layout (coordinates in micrometers) |
41
+ | `add_box(layer, x1, y1, x2, y2, datatype)` | Add a rectangle |
42
+ | `add_polygon(layer, points, datatype)` | Add a polygon from `[x, y]` vertices |
43
+ | `add_path(layer, points, width, datatype)` | Add a path with width |
44
+ | `add_label(layer, x, y, text, datatype)` | Add a text label |
45
+ | `layout_info()` | Inspect current layout (layers, bbox, shape count) |
46
+ | `save_gds(path, open_after)` | Write GDS/OASIS; optionally open in the editor |
47
+ | `open_layout(file_path)` | Open a file in KLayout (viewer) |
48
+ | `open_editor(file_path?)` | Open KLayout in editor mode, or a blank layout |
49
+ | `run_script(code)` | Run arbitrary Python with `klayout.db` (cell hierarchy, arrays, booleans, DRC, ...) |
50
+
51
+ ## Requirements
52
+
53
+ - Python ≥ 3.10
54
+ - The `klayout` and `mcp` Python packages (installed via the steps below)
55
+ - The [KLayout application](https://www.klayout.de/build.html) — only needed for `open_layout` / `open_editor`
56
+
57
+ ## Install
58
+
59
+ ```bash
60
+ uv sync
61
+ ```
62
+
63
+ ## Register with Claude Code
64
+
65
+ ```bash
66
+ claude mcp add klayout -s user -- "<repo>/.venv/Scripts/python.exe" -m klayout_draw_mcp.server
67
+ ```
68
+
69
+ On macOS/Linux use `.venv/bin/python`.
70
+
71
+ ## Usage
72
+
73
+ > "Draw a 5×5 µm box on layer 1 and a 2 µm-wide path, then save to out.gds and open it."
74
+
75
+ Claude calls `new_layout` → `add_box` / `add_path` → `save_gds(open_after=True)`.
76
+
77
+ For anything beyond the basic shapes, `run_script` exposes the full `klayout.db` API.
78
+
79
+ ## Examples
80
+
81
+ Self-contained scripts in [`examples/`](examples). Each builds a layout with the
82
+ standalone `klayout.db` module and writes a GDS:
83
+
84
+ ```bash
85
+ uv run python examples/cis_aps_pixel.py out.gds
86
+ ```
87
+
88
+ You can also paste the body of a script's `build()` function into the `run_script`
89
+ tool. The scripts share one layer map (OD 3/0, POLY 6/0, NPLUS 4/0, METAL1 9/0, ...).
90
+
91
+ | Example | What it draws |
92
+ | --- | --- |
93
+ | `basic_shapes.py` | The four primitives: box, path, polygon, label |
94
+ | `nmos_transistor.py` | A single NMOS (active, poly gate, n+, contacts, M1 S/G/D) |
95
+ | `cmos_inverter.py` | A CMOS inverter: PMOS over NMOS, shared gate, Vdd/Vss rails |
96
+ | `cis_aps_pixel.py` | A 1 µm 4T CMOS image-sensor pixel, tiled as a 2×2 array |
97
+
98
+ ## Note
99
+
100
+ `run_script` executes arbitrary Python locally, in-process. Only run scripts you trust.
@@ -0,0 +1,71 @@
1
+ # klayout-draw-mcp
2
+
3
+ An [MCP](https://modelcontextprotocol.io) server for **drawing GDS layouts** with **KLayout** — generate, edit, and view layouts from an AI assistant such as Claude Code or Claude Desktop.
4
+
5
+ Layout geometry is built in-process with the standalone [`klayout`](https://pypi.org/project/klayout/) Python module (no GUI needed). The installed KLayout application is launched only for viewing or manual editing.
6
+
7
+ ## Tools
8
+
9
+ | Tool | Purpose |
10
+ | --- | --- |
11
+ | `new_layout(top_cell, dbu)` | Start a new in-memory layout (coordinates in micrometers) |
12
+ | `add_box(layer, x1, y1, x2, y2, datatype)` | Add a rectangle |
13
+ | `add_polygon(layer, points, datatype)` | Add a polygon from `[x, y]` vertices |
14
+ | `add_path(layer, points, width, datatype)` | Add a path with width |
15
+ | `add_label(layer, x, y, text, datatype)` | Add a text label |
16
+ | `layout_info()` | Inspect current layout (layers, bbox, shape count) |
17
+ | `save_gds(path, open_after)` | Write GDS/OASIS; optionally open in the editor |
18
+ | `open_layout(file_path)` | Open a file in KLayout (viewer) |
19
+ | `open_editor(file_path?)` | Open KLayout in editor mode, or a blank layout |
20
+ | `run_script(code)` | Run arbitrary Python with `klayout.db` (cell hierarchy, arrays, booleans, DRC, ...) |
21
+
22
+ ## Requirements
23
+
24
+ - Python ≥ 3.10
25
+ - The `klayout` and `mcp` Python packages (installed via the steps below)
26
+ - The [KLayout application](https://www.klayout.de/build.html) — only needed for `open_layout` / `open_editor`
27
+
28
+ ## Install
29
+
30
+ ```bash
31
+ uv sync
32
+ ```
33
+
34
+ ## Register with Claude Code
35
+
36
+ ```bash
37
+ claude mcp add klayout -s user -- "<repo>/.venv/Scripts/python.exe" -m klayout_draw_mcp.server
38
+ ```
39
+
40
+ On macOS/Linux use `.venv/bin/python`.
41
+
42
+ ## Usage
43
+
44
+ > "Draw a 5×5 µm box on layer 1 and a 2 µm-wide path, then save to out.gds and open it."
45
+
46
+ Claude calls `new_layout` → `add_box` / `add_path` → `save_gds(open_after=True)`.
47
+
48
+ For anything beyond the basic shapes, `run_script` exposes the full `klayout.db` API.
49
+
50
+ ## Examples
51
+
52
+ Self-contained scripts in [`examples/`](examples). Each builds a layout with the
53
+ standalone `klayout.db` module and writes a GDS:
54
+
55
+ ```bash
56
+ uv run python examples/cis_aps_pixel.py out.gds
57
+ ```
58
+
59
+ You can also paste the body of a script's `build()` function into the `run_script`
60
+ tool. The scripts share one layer map (OD 3/0, POLY 6/0, NPLUS 4/0, METAL1 9/0, ...).
61
+
62
+ | Example | What it draws |
63
+ | --- | --- |
64
+ | `basic_shapes.py` | The four primitives: box, path, polygon, label |
65
+ | `nmos_transistor.py` | A single NMOS (active, poly gate, n+, contacts, M1 S/G/D) |
66
+ | `cmos_inverter.py` | A CMOS inverter: PMOS over NMOS, shared gate, Vdd/Vss rails |
67
+ | `cis_aps_pixel.py` | A 1 µm 4T CMOS image-sensor pixel, tiled as a 2×2 array |
68
+
69
+ ## Note
70
+
71
+ `run_script` executes arbitrary Python locally, in-process. Only run scripts you trust.
@@ -0,0 +1,64 @@
1
+ # Examples
2
+
3
+ Self-contained scripts live in [`examples/`](https://github.com/geniuskey/klayout-draw-mcp/tree/main/examples).
4
+ Each one builds a layout with the standalone `klayout.db` module and writes a GDS:
5
+
6
+ ```bash
7
+ uv run python examples/cis_aps_pixel.py out.gds
8
+ ```
9
+
10
+ You can also paste the body of a script's `build()` function into the `run_script`
11
+ tool. All examples share one layer map (OD `3/0`, POLY `6/0`, NPLUS `4/0`,
12
+ PPLUS `5/0`, METAL1 `9/0`, NWELL `1/0`, PWELL `2/0`, CONT `8/0`, TEXT `63/0`, …).
13
+
14
+ The screenshots below are rendered headlessly with `klayout.lay` by
15
+ [`scripts/render_examples.py`](https://github.com/geniuskey/klayout-draw-mcp/blob/main/scripts/render_examples.py),
16
+ the same way the documentation builds them in CI.
17
+
18
+ ## Basic shapes
19
+
20
+ The four primitives — box, path, polygon and label — on a few layers. The place to
21
+ start if you just want to see geometry come out.
22
+
23
+ ![Basic shapes layout](assets/images/basic_shapes.png){ loading=lazy }
24
+
25
+ ```python title="examples/basic_shapes.py"
26
+ --8<-- "examples/basic_shapes.py"
27
+ ```
28
+
29
+ ## NMOS transistor
30
+
31
+ A single planar n-channel MOSFET (W = 1 µm, L = 0.4 µm): active diffusion, a poly
32
+ gate crossing the channel, n+ source/drain implant, and contacts plus metal-1 pads
33
+ for source (S), gate (G) and drain (D).
34
+
35
+ ![NMOS transistor layout](assets/images/nmos_transistor.png){ loading=lazy }
36
+
37
+ ```python title="examples/nmos_transistor.py"
38
+ --8<-- "examples/nmos_transistor.py"
39
+ ```
40
+
41
+ ## CMOS inverter
42
+
43
+ A pull-up PMOS (in an n-well, top) over a pull-down NMOS (in the p-well, bottom),
44
+ sharing one poly gate (the input, in red). The two drains are tied together on
45
+ metal-1 (the output). Vdd rails the top, Vss the bottom, with well ties on each.
46
+
47
+ ![CMOS inverter layout](assets/images/cmos_inverter.png){ loading=lazy }
48
+
49
+ ```python title="examples/cmos_inverter.py"
50
+ --8<-- "examples/cmos_inverter.py"
51
+ ```
52
+
53
+ ## CIS APS pixel
54
+
55
+ A simplified 1 µm 4T CMOS image-sensor pixel: a large photodiode (PD), a transfer
56
+ gate (TX) to the floating diffusion (FD), and reset (RST), source-follower (SF) and
57
+ row-select (SEL) transistors in the shared active column. The unit pixel is drawn
58
+ into its own cell and instanced as a 2×2 array, the way a real sensor array repeats.
59
+
60
+ ![CIS APS pixel 2x2 array](assets/images/cis_aps_pixel.png){ loading=lazy }
61
+
62
+ ```python title="examples/cis_aps_pixel.py"
63
+ --8<-- "examples/cis_aps_pixel.py"
64
+ ```
@@ -0,0 +1,64 @@
1
+ # klayout-draw-mcp
2
+
3
+ [![PyPI](https://img.shields.io/pypi/v/klayout-draw-mcp.svg)](https://pypi.org/project/klayout-draw-mcp/)
4
+ [![Python](https://img.shields.io/pypi/pyversions/klayout-draw-mcp.svg)](https://pypi.org/project/klayout-draw-mcp/)
5
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/geniuskey/klayout-draw-mcp/blob/main/LICENSE)
6
+
7
+ An [MCP](https://modelcontextprotocol.io) server for **drawing GDS layouts** with
8
+ **[KLayout](https://www.klayout.de)** — generate, edit and view chip layouts straight
9
+ from an AI assistant such as Claude Code or Claude Desktop.
10
+
11
+ Layout geometry is built in-process with the standalone
12
+ [`klayout`](https://pypi.org/project/klayout/) Python module — no GUI needed. The
13
+ installed KLayout application is launched only when you want to view or hand-edit a
14
+ result.
15
+
16
+ > "Draw a CIS 4T APS pixel at 1 µm pitch, tile it 2×2, then save to `out.gds` and open it."
17
+
18
+ The assistant calls `new_layout` → drawing tools / `run_script` → `save_gds(open_after=True)`.
19
+ See the [Examples](examples.md) for layouts produced exactly this way.
20
+
21
+ ## Tools
22
+
23
+ | Tool | Purpose |
24
+ | --- | --- |
25
+ | `new_layout(top_cell, dbu)` | Start a new in-memory layout (coordinates in micrometers) |
26
+ | `add_box(layer, x1, y1, x2, y2, datatype)` | Add a rectangle |
27
+ | `add_polygon(layer, points, datatype)` | Add a polygon from `[x, y]` vertices |
28
+ | `add_path(layer, points, width, datatype)` | Add a path with width |
29
+ | `add_label(layer, x, y, text, datatype)` | Add a text label |
30
+ | `layout_info()` | Inspect current layout (layers, bbox, shape count) |
31
+ | `save_gds(path, open_after)` | Write GDS/OASIS; optionally open in the editor |
32
+ | `open_layout(file_path)` | Open a file in KLayout (viewer) |
33
+ | `open_editor(file_path?)` | Open KLayout in editor mode, or a blank layout |
34
+ | `run_script(code)` | Run arbitrary Python with `klayout.db` (cell hierarchy, arrays, booleans, DRC, …) |
35
+
36
+ ## Install
37
+
38
+ ```bash
39
+ pip install klayout-draw-mcp
40
+ ```
41
+
42
+ The [KLayout application](https://www.klayout.de/build.html) is only required for
43
+ `open_layout` / `open_editor`.
44
+
45
+ ## Register with Claude Code
46
+
47
+ ```bash
48
+ claude mcp add klayout -s user -- python -m klayout_draw_mcp.server
49
+ ```
50
+
51
+ When running from a checkout, point at the project's interpreter instead, e.g.
52
+ `<repo>/.venv/Scripts/python.exe` on Windows or `<repo>/.venv/bin/python` on macOS/Linux.
53
+
54
+ ## How it works
55
+
56
+ There are two complementary ways to produce geometry:
57
+
58
+ 1. **High-level drawing tools** (`new_layout`, `add_box`, `add_polygon`, …) build a
59
+ layout step by step. All coordinates are in micrometers.
60
+ 2. **`run_script`** executes arbitrary Python with `klayout.db` available, for anything
61
+ the high-level tools do not cover — cell hierarchy, array instances, boolean ops, DRC.
62
+
63
+ !!! warning
64
+ `run_script` executes arbitrary Python locally, in-process. Only run scripts you trust.
@@ -0,0 +1,67 @@
1
+ """Basic shapes sample for klayout-draw-mcp.
2
+
3
+ Shows the four primitives (box, path, polygon, label) on a few layers.
4
+
5
+ Run standalone:
6
+ uv run python examples/basic_shapes.py [out.gds]
7
+
8
+ Or paste the body of ``build()`` into the ``run_script`` MCP tool (``db`` is
9
+ injected there; ``ly``/``top`` map to ``session.layout``/``session.top``).
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import klayout.db as db
15
+
16
+ # Shared layer map used across the examples: name -> (layer, datatype)
17
+ LAYERS = {
18
+ "OD": (3, 0), # active / diffusion
19
+ "POLY": (6, 0), # gate poly
20
+ "M1": (9, 0), # metal 1
21
+ "TEXT": (63, 0), # labels
22
+ }
23
+
24
+
25
+ def build() -> db.Layout:
26
+ ly = db.Layout()
27
+ ly.dbu = 0.001 # 1 nm grid
28
+ top = ly.create_cell("BASIC")
29
+
30
+ def lay(name: str) -> int:
31
+ return ly.layer(*LAYERS[name])
32
+
33
+ def box(name, x1, y1, x2, y2):
34
+ top.shapes(lay(name)).insert(db.DBox(x1, y1, x2, y2))
35
+
36
+ def path(name, pts, width):
37
+ top.shapes(lay(name)).insert(db.DPath([db.DPoint(*p) for p in pts], width))
38
+
39
+ def polygon(name, pts):
40
+ top.shapes(lay(name)).insert(db.DPolygon([db.DPoint(*p) for p in pts]))
41
+
42
+ def label(name, x, y, text):
43
+ top.shapes(lay(name)).insert(db.DText(text, db.DTrans(db.DVector(x, y))))
44
+
45
+ # a rectangle
46
+ box("OD", 0.0, 0.0, 5.0, 5.0)
47
+
48
+ # a 0.5 um wide L-shaped path
49
+ path("M1", [(0.0, 6.0), (5.0, 6.0), (5.0, 10.0)], 0.5)
50
+
51
+ # a triangle polygon
52
+ polygon("POLY", [(7.0, 0.0), (12.0, 0.0), (9.5, 5.0)])
53
+
54
+ # a couple of labels
55
+ label("TEXT", 2.5, 2.5, "box")
56
+ label("TEXT", 9.5, 1.5, "polygon")
57
+
58
+ return ly
59
+
60
+
61
+ if __name__ == "__main__":
62
+ import sys
63
+
64
+ out = sys.argv[1] if len(sys.argv) > 1 else "basic_shapes.gds"
65
+ layout = build()
66
+ layout.write(out)
67
+ print(f"wrote {out} bbox={layout.top_cell().dbbox().to_s()} um")
@@ -0,0 +1,128 @@
1
+ """CIS 4T active-pixel-sensor (APS) sample for klayout-draw-mcp.
2
+
3
+ A simplified 1 um 4T CMOS image-sensor pixel: a large photodiode (PD), a
4
+ transfer gate (TX) to the floating diffusion (FD), and reset (RST),
5
+ source-follower (SF) and row-select (SEL) transistors in the shared active
6
+ column. The unit pixel is drawn into its own cell and instanced as a 2x2
7
+ array, the way a real sensor array repeats.
8
+
9
+ Run standalone:
10
+ uv run python examples/cis_aps_pixel.py [out.gds]
11
+
12
+ Or paste the body of ``build()`` into the ``run_script`` MCP tool.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import klayout.db as db
18
+
19
+ # Shared layer map: name -> (layer, datatype)
20
+ LAYERS = {
21
+ "PWELL": (2, 0),
22
+ "OD": (3, 0), # active / diffusion
23
+ "NPLUS": (4, 0), # n+ implant
24
+ "PPLUS": (5, 0), # p+ well tap
25
+ "POLY": (6, 0), # gate poly (TX / RST / SF / SEL)
26
+ "CONT": (8, 0),
27
+ "M1": (9, 0),
28
+ "PD": (10, 0), # photodiode n-implant
29
+ "TEXT": (63, 0),
30
+ "BND": (100, 0), # pixel boundary marker
31
+ }
32
+
33
+ PIXEL_PITCH = 1.0 # um
34
+ ARRAY = (2, 2) # columns, rows
35
+
36
+
37
+ def build() -> db.Layout:
38
+ ly = db.Layout()
39
+ ly.dbu = 0.001
40
+ top = ly.create_cell("CIS_APS")
41
+ pix = ly.create_cell("APS_PIXEL")
42
+
43
+ def lay(name: str) -> int:
44
+ return ly.layer(*LAYERS[name])
45
+
46
+ def box(name, x1, y1, x2, y2):
47
+ pix.shapes(lay(name)).insert(db.DBox(x1, y1, x2, y2))
48
+
49
+ def label(name, x, y, text):
50
+ pix.shapes(lay(name)).insert(db.DText(text, db.DTrans(db.DVector(x, y))))
51
+
52
+ def cont(cx, cy):
53
+ box("CONT", cx - 0.02, cy - 0.02, cx + 0.02, cy + 0.02)
54
+
55
+ # pixel boundary + well
56
+ box("BND", 0.0, 0.0, 1.0, 1.0)
57
+ box("PWELL", 0.0, 0.0, 1.0, 1.0)
58
+
59
+ # photodiode (left, large fill factor)
60
+ box("PD", 0.05, 0.05, 0.55, 0.95)
61
+ box("OD", 0.08, 0.08, 0.52, 0.92)
62
+
63
+ # transfer gate TX, bridging PD -> FD
64
+ box("POLY", 0.50, 0.58, 0.60, 0.82)
65
+
66
+ # floating diffusion FD
67
+ box("OD", 0.58, 0.60, 0.70, 0.80)
68
+ box("NPLUS", 0.57, 0.59, 0.71, 0.81)
69
+
70
+ # shared transistor active column on the right
71
+ box("OD", 0.70, 0.06, 0.94, 0.94)
72
+ box("NPLUS", 0.69, 0.05, 0.95, 0.95)
73
+
74
+ # RST / SF / SEL gates (horizontal poly across the column)
75
+ box("POLY", 0.66, 0.66, 0.97, 0.74) # RST
76
+ box("POLY", 0.66, 0.40, 0.97, 0.48) # SF
77
+ box("POLY", 0.66, 0.16, 0.97, 0.24) # SEL
78
+
79
+ # p-well tap (Vss), bottom-left
80
+ box("OD", 0.10, 0.10, 0.24, 0.20)
81
+ box("PPLUS", 0.09, 0.09, 0.25, 0.21)
82
+
83
+ # contacts
84
+ cont(0.64, 0.70) # FD
85
+ cont(0.18, 0.15) # Vss tap
86
+ for cy in (0.11, 0.32, 0.57, 0.84): # right column source/drains
87
+ cont(0.82, cy)
88
+ cont(0.69, 0.70) # RST gate
89
+ cont(0.69, 0.44) # SF gate
90
+ cont(0.69, 0.20) # SEL gate
91
+
92
+ # metal-1: FD -> SF gate strap, and a Vdd rail
93
+ box("M1", 0.62, 0.68, 0.72, 0.72)
94
+ box("M1", 0.66, 0.44, 0.72, 0.72)
95
+ box("M1", 0.88, 0.06, 0.95, 0.94)
96
+
97
+ # labels
98
+ label("TEXT", 0.26, 0.48, "PD")
99
+ label("TEXT", 0.50, 0.86, "TX")
100
+ label("TEXT", 0.60, 0.83, "FD")
101
+ label("TEXT", 0.74, 0.69, "RST")
102
+ label("TEXT", 0.76, 0.43, "SF")
103
+ label("TEXT", 0.74, 0.19, "SEL")
104
+ label("TEXT", 0.10, 0.23, "Vss")
105
+ label("TEXT", 0.90, 0.965, "Vdd")
106
+
107
+ # tile the unit pixel into a 2x2 array
108
+ nx, ny = ARRAY
109
+ top.insert(
110
+ db.DCellInstArray(
111
+ pix.cell_index(),
112
+ db.DTrans(db.DVector(0.0, 0.0)),
113
+ db.DVector(PIXEL_PITCH, 0.0),
114
+ db.DVector(0.0, PIXEL_PITCH),
115
+ nx,
116
+ ny,
117
+ )
118
+ )
119
+ return ly
120
+
121
+
122
+ if __name__ == "__main__":
123
+ import sys
124
+
125
+ out = sys.argv[1] if len(sys.argv) > 1 else "cis_aps.gds"
126
+ layout = build()
127
+ layout.write(out)
128
+ print(f"wrote {out} bbox={layout.top_cell().dbbox().to_s()} um")