File formats
reticle-io reads and writes the layout interchange formats and the technology
description.
GDSII
GDSII is the long-standing binary interchange format for IC layout. Reticle reads
and writes it through gds21, the de facto Rust GDSII library (part of the
Layout21 project), mapping its structures, boundaries, paths, and references onto
Reticle cells, shapes, instances, and arrays. Round-trip fidelity is tested: a
document exported to GDSII and re-imported preserves its geometry, layers, and
hierarchy.
The Reticle container format (OASIS-inspired, ADR 0004)
OASIS is the newer, compressed successor to GDSII, but it has no mature Rust
library. Reticle’s Oasis type is therefore not a conformant OASIS reader or
writer: it is an in-house binary container, OASIS-inspired (it borrows the
spirit - a magic string, a START/END frame, and CELL/RECTANGLE/POLYGON/
PATH/TEXT/PLACEMENT/ARRAY records with explicit layer and datatype) but not
its wire format. No third-party tool (KLayout, gdstk) can read it. It exists to
round-trip Reticle’s own geometry and hierarchy compactly and losslessly for the
supported record set; anything unsupported is a clear error rather than silent data
loss. See ADR 0004 for the container layout and honest gaps.
Conformant OASIS reader and writer (oasis_std)
Separately, OasisStd is a genuine SEMI P39 OASIS reader and writer for a
practical subset - its writer output is read by KLayout as OASIS, and its reader
parses that same subset back into a Document, so a document survives a
write-then-read round trip. The writer is uncompressed (no CBLOCK), emitting
RECTANGLE, POLYGON, PATH, PLACEMENT, and TEXT with fully explicit modal
state, CELLNAME+CELL tables, and PLACEMENT records carrying magnification and
angle. The reader additionally decodes third-party OASIS constructs real PDKs and
KLayout emit that the writer itself never produces: CBLOCK-compressed blocks,
repetition records, and the short point-list forms. Documented writer-subset gaps:
arrays are expanded to individual placements, a label’s anchor is dropped (OASIS
TEXT is a point), and a path’s round end cap is written flush. KLayout reading
oasis_std output is verified in-container by the interop harness; see the
interop chapter and ADR 0086. The reader is wired into the app’s own
open path (crates/reticle-app/src/open.rs), so opening a .oas/.oasis file
whose bytes carry the conformant magic uses this reader, not the in-house
container above. Check:
cargo nextest run -p reticle-io --test oasis_std_read.
CIF
CIF (Caltech Intermediate Format) import reads the classic CIF 2.0 primitive subset
used by MOSIS-era mask layouts: symbol definitions (DS/DF), layers (L), boxes
(B), polygons (P), wires (W), and symbol calls with transforms (C). Import
only; there is no CIF exporter. An
unrecognized top-level command is skipped with a warning rather than rejected, so one
private extension statement does not sink an otherwise good file. See
crates/reticle-io/src/cif.rs for the full grammar and its honest gaps (CIF
text/label extensions and rounded-flash geometry are not implemented).
DXF
DXF (Drawing Exchange Format) import reads the layout-relevant 2D subset of the
ENTITIES section: LINE, LWPOLYLINE, the classic POLYLINE/VERTEX/SEQEND
chain, CIRCLE, ARC, and the common polyline-boundary case of HATCH. Import
only; there is no DXF exporter. Every other entity type (TEXT, INSERT, SPLINE,
…) is recognized structurally, so it cannot desync the parser, and is skipped with
a deduped warning. See crates/reticle-io/src/dxf.rs for the full entity list and
its honest gaps (a bulged polyline imports as its straight-edged chord, not its
rounded outline).
Magic
Magic (.mag) import reads the classic ASCII per-cell subset: a header, `<< layer
sections carryingrectgeometry, anduseplacements of other (by-name) cells. Import only; there is no Magic exporter. A.magfile describes exactly one cell, so ausebecomes an instance naming the target cell by string, without that cell's own geometry present in the document - real Magic resolves auseat read time from a directory of sibling files, which is out of scope for a reader that sees only one file's bytes. Seecrates/reticle-io/src/mag.rsfor the full grammar and its honest gaps (onlyrectgeometry; notri,polygon`, or label elements).
Technology files
A technology file describes the process: the database resolution, the layer table
(numbers, datatypes, names, and display colors), and the design rules. Reticle
parses a simple, readable text format into the model’s Technology, which then
drives layer display and the design-rule checker.
Streaming a die: the record reader and the .rtla builder
The GDSII importer above reads a whole library into memory under a 256 MiB cap. A full shuttle die is several gigabytes and the in-browser converter runs in a worker, so both need to pull one record at a time without ever holding the whole file. Two pieces make that possible (Wave 2, ADR 0062 and ADR 0063).
GdsRecordReader<R: Read> is a forward-only GDSII reader over any byte source. It
hand-rolls the record framing (2-byte length, record type, data type, payload) with
no gds21 dependency, so it is wasm-clean, and it yields a small flat vocabulary of
GdsEvents (library and struct boundaries, boundaries, paths, references, arrays,
text) in document order. It carries the same hardening as the DOM importer: a
zero-length string record is rejected before anyone can index data[-1], dates are
skipped rather than parsed (so the out-of-range-date panic class cannot fire), and a
record length is a 16-bit field, so no count ever drives an allocation past the
remaining input. A differential test asserts the streaming reader accepts everything
the DOM importer accepts and reports the same cells and per-layer shape counts across
the real corpora; a fuzz target (gds_stream) drives it over arbitrary bytes.
build_rtla writes a .rtla streamed archive from a lazy record source using bounded
memory. It is external and two-pass: pass 1 streams the records and spills them to
sorted run files on disk; pass 2 merges the runs and emits the tiles in directory
order, holding at most one sort chunk and one tile in memory. The finest pyramid level
is exact (every record reaches it and round-trips); coarser levels are subsampled
paint-only approximations. On the 30M-entry generated layout the build peaks at
127 MiB of RSS (far under a 2 GiB budget), and a 120M-record build produces a 2.42 GB
archive to completion under the same bound. The on-disk framing (a 32-byte preamble
locating the rkyv header and directory blocks, then byte-contiguous tiles) is
specified in ADR 0063 so the native and wasm tile sources read to the same layout.
Converting a GDS to a streamable archive
reticle convert <in.gds> <out.rtla> joins those two pieces into one command: it turns
a GDSII file into a .rtla archive you can browse over an HTTP range or a memory map
without ever loading the whole die. It streams the input twice, holding no whole-file
model. Pass one scans every record to find the world bounding box, recover the database
scale, and count the drawn elements; pass two reopens the file as a lazy record source
and feeds it to build_rtla. Peak memory is the builder’s spill budget, not the file
size, so the command scales to the same gigabyte dies the builder targets. The
conversion is byte-deterministic: records are emitted in document order, the world box
is an order-independent union, the pyramid depth is a pure function of the world span,
and the builder writes no timestamps, so the same input always produces identical
bytes.
Its v1 flatten scope is deliberately narrow: only directly drawn geometry becomes a record. Each boundary and path bounding box is one tile record, in database units as authored (a path is inflated by half its width). Instance and array references are not composed into world space, because expanding a placement needs random access to the referenced cell’s geometry (a whole-file model), the very thing the streaming path avoids. A referenced cell’s own shapes are still captured where they are drawn, in that cell’s own frame, so a flat or already-flattened GDS (what the tile generator and the export path emit) converts faithfully, while a deeply hierarchical one does not yet reproduce its placements. True hierarchical flattening is a documented follow-up. See ADR 0072 for the flatten and leveling choices.
Robustness
The parsers are fuzzed. A parser must never panic or hang on malformed input; it
either produces a document or returns an error. The streaming GdsRecordReader holds
the same guarantee: its gds_stream fuzz target seeds from the committed GDS crash
fixtures so it cannot reintroduce a fixed panic class, and a native regression test
drives it over those fixtures on every platform (libFuzzer cannot link on MSVC).