Put a book on a chip! Create silicon art with text visible under a microscope.
This project demonstrates how to create silicon art using TinyTapeout - putting text, characters, or even entire books onto a silicon wafer that can be viewed under a microscope.
- Text rendered as metal polygons on silicon
- Characters approximately 5-15 micrometers tall (depending on tile size)
- Visible under an optical microscope at 50-500x magnification
- A real fabricated chip with your art!
├── src/
│ ├── project.v # Minimal Verilog design (required by TinyTapeout)
│ ├── config.json # OpenLane configuration
│ ├── text_to_gds.py # Generate standalone text GDS
│ └── add_text_to_gds.py # Add text to existing GDS (post-process)
├── test/
│ ├── test.py # Cocotb testbench
│ └── Makefile
├── docs/
│ └── info.md # Project documentation
├── .github/workflows/
│ ├── gds.yaml # Build GDS + viewer
│ ├── test.yaml # Run tests
│ └── docs.yaml # Build docs
├── info.yaml # TinyTapeout project info
└── README.md
TinyTapeout uses the OpenLane flow to synthesize Verilog into a GDS (GDSII) file - the format used for chip fabrication. The GDS contains multiple layers representing different materials on the silicon.
For silicon art, we:
- Create a minimal valid Verilog design (required for TinyTapeout submission)
- Generate text as polygons on metal layers
- These metal layers are physically created during fabrication
- The metal patterns are visible under a microscope!
| Layer | GDS # | Notes |
|---|---|---|
| met1 | 68:20 | Closest to silicon, most visible |
| met2 | 69:20 | Good visibility |
| met3 | 70:20 | Good visibility |
| met4 | 71:20 | Top layer available in TinyTapeout |
| li1 | 67:20 | Local interconnect |
TinyTapeout offers different tile sizes. Dimensions vary by PDK:
IHP-SG13G2 PDK:
| Size | Dimensions | Approximate Text Capacity |
|---|---|---|
| 1x1 | 202 × 155 µm | ~25 characters |
| 1x2 | 202 × 314 µm | ~60 characters |
| 2x2 | 420 × 314 µm | ~120 characters |
Sky130 PDK:
| Size | Dimensions | Approximate Text Capacity |
|---|---|---|
| 1x1 | 161 × 112 µm | ~20 characters |
| 1x2 | 161 × 226 µm | ~50 characters |
| 2x2 | 335 × 226 µm | ~100 characters |
# Install dependencies
pip install gdstk
# Generate Hello World GDS
cd src
python text_to_gds.py --hello-world
# Or custom text
python text_to_gds.py "Hello\nWorld!" hello.gdsAfter pushing to GitHub, the GDS viewer will be available at:
https://YOUR_USERNAME.github.io/YOUR_REPO/
Or locally with KLayout:
pip install klayout
klayout text_art.gds# Simple text
python text_to_gds.py "Your Name" name.gds
# Multi-line
python text_to_gds.py "Line 1\nLine 2\nLine 3" multiline.gds
# Larger tile for more text
python text_to_gds.py --tile-size 2x2 "More space!" large.gds
# From a file (for books/long text)
python text_to_gds.py --tile-size 8x2 --from-file book.txt book.gds
# Different metal layer
python text_to_gds.py --layer met2 "On Metal 2" met2.gds
# Custom font size (in micrometers)
python text_to_gds.py --font-size 3.0 "Tiny Text" tiny.gdsIf you have a GDS from OpenLane and want to add art:
python add_text_to_gds.py runs/wokwi/results/final/gds/tt_um_example.gds \
output.gds "Hello World!" --layer met4- Fork this repository
- Modify
src/text_to_gds.pyto generate your desired text - Update
info.yamlwith your project details - Push to GitHub - the Actions will build everything
- Submit to TinyTapeout
- Create your functional Verilog design in
src/project.v - After the GDS is built, use
add_text_to_gds.pyto add art - The text will be added to an upper metal layer (met4)
For advanced users, you can integrate the text GDS as a macro:
- Generate text GDS with
text_to_gds.py - Modify OpenLane config to include it as
EXTRA_GDS_FILES - This merges the art directly into the build
Once fabricated, you can view your silicon art with:
-
Optical Microscope: 50-500x magnification
- Metallurgical/reflected light microscope works best
- Dark field illumination enhances metal visibility
-
Electron Microscope: For ultimate detail
- SEM (Scanning Electron Microscope) for surface detail
- University labs often have access
The metal layers appear as:
- Bright/reflective areas under optical microscope
- Different metal layers have slightly different colors
- Text will be approximately 5-20 µm tall depending on settings
Yes, you can put actual book content on silicon! Here's how:
# Prepare your text file
echo "It was the best of times..." > excerpt.txt
# Generate with largest tile size
python text_to_gds.py --tile-size 8x2 --from-file excerpt.txt book.gdsCapacity estimates (approximate, depends on font size):
- 1x1 tile: ~50-100 bytes of text
- 8x2 tile: ~1-2 KB of text
For larger texts, consider:
- Multiple tiles
- Smaller font sizes (minimum ~1-2 µm)
- Abbreviations or encoding schemes
The text_to_gds.py script uses gdstk to:
- Render text using a built-in font
- Convert characters to polygon paths
- Place polygons on the specified metal layer
- Output a GDSII file
- Aspect ratio: 1:2 (width:height)
- Character spacing: 9/16 × font_size
- Line spacing: 5/4 × font_size
- Minimum recommended size: 2 µm
IHP-SG13G2 DRC Rules:
- Metal1: min width 0.16µm, min space 0.18µm, density 35-60%
- Metal2-4: min width 0.20µm, min space 0.21µm, density 35-60%
- TopMetal1: min width 1.64µm, min space 1.64µm, density 25-70%
The TinyTapeout precheck only allows specific layer/datatype combinations.
For IHP-SG13G2, .filler layers (datatype 22) are NOT in the whitelist.
| Layer | Allowed Datatypes | Fabricated? | DRC? |
|---|---|---|---|
| Metal1.drawing | 8/0 | ✅ Yes | ✅ Width/space |
| Metal1.pin | 8/2 | ✅ Yes | Pin only |
| Metal1.text | 8/25 | ❌ No | N/A |
| Metal1.filler | 8/22 | Would be... | ❌ NOT ALLOWED |
Practical approach for silicon art on IHP:
- Pixel art works: Use large rectangles (>0.20µm) on
.drawinglayers - Fine text doesn't work: gdstk.text() strokes often violate DRC
- Density is a concern: Sparse art may fail 35-60% density requirement
This project uses pixel art (large rectangles) which meets DRC requirements.
- TinyTapeout - Submit your design
- TinyTapeout Discord - Community support
- Sky130 PDK - Process documentation
- gdstk Documentation - GDS library
- KLayout - GDS viewer/editor
from text_to_gds import SiliconTextGenerator
gen = SiliconTextGenerator(tile_size="1x1", layer="met1")
cell = gen.create_text_cell("Hello\nWorld!")
gen.add_border(cell)
gen.save("hello_world.gds")gen = SiliconTextGenerator(tile_size="8x2", layer="met1")
text = """In the beginning
was the Word,
and the Word was
with God..."""
cell = gen.create_text_cell(text, font_size=8)
gen.add_border(cell, border_width=3)
gen.save("genesis.gds")Die area mismatch: expected 202.08x154.98 µm (1x1 tiles), got 161x111.52 µm
This project was originally written for Sky130 PDK but TinyTapeout shuttles may use IHP-SG13G2 PDK which has completely different tile dimensions:
| PDK | 1x1 Tile Size | Pin Y Position | Layer Name |
|---|---|---|---|
| Sky130 | 161.0 × 111.52 µm | 111.02 µm | met4 (lowercase) |
| IHP-SG13G2 | 202.08 × 154.98 µm | 154.48 µm | Metal4 (capital M) |
To fix for IHP PDK:
-
Die dimensions: Updated from 161×111.52 to 202.08×154.98 µm
-
PIN_Y_CENTER: Changed from 111.02 to 154.48 µm (pins at top edge)
-
All 43 signal pin X positions: Completely different coordinates extracted from:
tt-support-tools/tech/ihp-sg13g2/def/tt_block_1x1_pgvdd.def -
LEF layer name: Changed from
met4toMetal4(IHP uses capital M) -
GDS layer numbers: Updated to IHP layer scheme
Check which PDK your TinyTapeout shuttle uses:
- Look at the error message dimensions
- Check
tt-support-tools/tech/<pdk>/tile_sizes.yamlfor your shuttle
src/create_silicon_art.py- Main generator with all dimensions/pinssrc/validate_lef.py- Local validatortest/test_validate_lef.py- Tests
Signal pins are at Y=154.48µm (center). X positions from DEF:
- clk: 187.20, ena: 191.04, rst_n: 183.36
- ui_in[0-7]: 179.52 → 152.64 (step -3.84)
- uio_in[0-7]: 148.80 → 121.92
- uo_out[0-7]: 118.08 → 91.20
- uio_out[0-7]: 87.36 → 60.48
- uio_oe[0-7]: 56.64 → 29.76
ERROR: KLayout SG13G2 DRC ❌ Fail: Klayout sg13g2 failed with 45 DRC violations
ERROR: Layer check ❌ Fail: Invalid layers in GDS: {(30, 22), (10, 22), (8, 22)}
- Invalid layers: TinyTapeout's precheck whitelist doesn't include
.fillerlayers (datatype 22) - DRC violations: Fine text strokes from gdstk.text() violate min width rules
- DBU precision: Floating-point issues with GDS database unit
TinyTapeout's layer whitelist is restrictive. For IHP-SG13G2:
| Layer | Allowed? | Fabricated? | Notes |
|---|---|---|---|
| Metal1.drawing (8/0) | ✅ Yes | ✅ Yes | DRC checked (width/space) |
| Metal1.pin (8/2) | ✅ Yes | ✅ Yes | For pins only |
| Metal1.text (8/25) | ✅ Yes | ❌ No | Documentation only |
| Metal1.filler (8/22) | ❌ No | Would be... | Not in whitelist! |
This means:
- Pixel art is possible using
.drawinglayers with DRC-compliant geometry - Fine text is difficult because strokes often violate DRC rules
- There's no "DRC-exempt" fabricated layer in the TinyTapeout whitelist
1. Use .drawing layers (datatype 0) - the only whitelisted fabricated layers:
# Valid and fabricated (but DRC checked)
TEXT_LAYER = 8 # Metal1
TEXT_DATATYPE = 0 # .drawing - TinyTapeout whitelisted2. Use pixel art instead of fine text:
- Each pixel must be >= 0.20µm (max of all metal min widths)
- Pixels must be spaced >= 0.21µm apart
- The pixel pig design uses ~7.6µm pixels, easily passing DRC
3. Fixed DBU precision:
lib = gdstk.Library(unit=1e-6, precision=1e-9) # DBU = 0.001 exactly| Layer Name | GDS | Purpose | Fabricated? |
|---|---|---|---|
| Metal1.drawing | 8/0 | Art + routing | ✅ Yes (DRC checked) |
| Metal1.pin | 8/2 | Pins | ✅ Yes |
| Metal1.text | 8/25 | Documentation | ❌ No |
| Metal4.pin | 50/2 | Signal pins | ✅ Yes |
| TopMetal1.pin | 126/2 | Power pins | ✅ Yes |
| prBoundary | 189/4 | Die boundary | N/A |
For TinyTapeout IHP, silicon art must use .drawing layers and meet DRC rules.
This limits options to:
- Pixel art with large (>0.2µm) rectangles ✅
- Large text with thick (>2µm) strokes ✅
- Fine detailed text ❌ (violates DRC)
librelane.flows.flow.FlowError: One or more deferred errors were encountered:
52 LVS errors found.
With warnings in the extraction log:
[!] Pin without name ignored !
[!] Pin without name ignored !
... (repeated 45 times)
And in the LVS report, pins appear with proxy prefix:
proxyuo_out[0] |uo_out[0]
proxyVPWR |VPWR
proxyVGND |VGND
Pin labels are on the wrong GDS layer! The SPICE extraction tool (used for LVS) only recognizes pin names on .text layers (datatype 25), NOT .label layers (datatype 1).
| Layer | Datatype 1 (.label) | Datatype 25 (.text) |
|---|---|---|
| Metal4 (50) | ❌ Not recognized | ✅ Used for signal pin names |
| TopMetal1 (126) | ❌ Not recognized | ✅ Used for power pin names |
When pin labels are on datatype 1, the extraction tool finds the pin shapes but can't find their names, causing:
- "Pin without name ignored" warnings
- All macro pins become undefined in the SPICE netlist
- LVS creates dummy
proxy*net names for wires to the missing pins - 52 LVS errors due to pin mismatches
Changed label datatype from 1 to 25 in GDS generation:
# WRONG - extraction tool ignores these
LABEL_DATATYPE = 1 # .label datatype (not recognized!)
POWER_LABEL_DATATYPE = 1 # .label datatype (not recognized!)
# CORRECT - extraction tool finds pin names
LABEL_DATATYPE = 25 # .text datatype ✅
POWER_LABEL_DATATYPE = 25 # .text datatype ✅import gdstk
lib = gdstk.read_gds('your_design.gds')
cell = lib.cells[0]
for label in cell.labels:
print(f'{label.text} at layer {label.layer}/{label.texttype}')
# Should show:
# clk at layer 50/25 (Metal4.text) ✅
# VPWR at layer 126/25 (TopMetal1.text) ✅
# NOT:
# clk at layer 50/1 (Metal4.label) ❌Apache 2.0 - See LICENSE
- TinyTapeout for making chip fabrication accessible
- Efabless and Google for the Sky130 shuttle program
- The open-source silicon community