219 lines
10 KiB
Markdown
219 lines
10 KiB
Markdown
# GDS and SVG Generation Logic Path
|
|
|
|
This document traces the current code path for generating saved layout YAML,
|
|
layout preview SVG, and downloadable project GDS when the user clicks
|
|
`Build Layout` or `Build GDS`.
|
|
|
|
Line numbers refer to the files as currently checked in.
|
|
|
|
## Build-Time Router Gate
|
|
|
|
`python backend/server.py` -> `app.run` starts without importing
|
|
`mxpic_router`, `mxpic_forge`, Nazca, or gdstk. Login, dashboard, canvas
|
|
editing, YAML generation, PDK browsing, and project save without preview do not
|
|
require the external build stack.
|
|
|
|
Build actions validate the external stack on demand:
|
|
|
|
- `Build GDS` -> `backend/gds_builder.py` -> `require_router_stack()`.
|
|
- `Build Layout` SVG preview -> `backend/routed_layout_preview.py` ->
|
|
`require_router_stack(require_gdstk=True)`.
|
|
|
|
Important functions:
|
|
|
|
- `ensure_router_path` (`backend/router_dependency.py` line 23) adds the sibling
|
|
`../mxpic_router` checkout to `sys.path` when present.
|
|
- `require_router_stack` (`backend/router_dependency.py` line 31) imports
|
|
`mxpic_router`, `nazca`, and the route backend used by `mxpic_router`.
|
|
The preferred route backend is `mxpic_forge.Route`; if it is absent,
|
|
`mxpic_router` falls back to Nazca `interconnects.Interconnect`.
|
|
`gdstk` is checked only when SVG preview generation requests it.
|
|
|
|
## Generated Files
|
|
|
|
- Saved cell YAML: `database/<username>/layout/<project>/<cell>.yml`
|
|
- Path helpers: `user_layout_root`, `project_root`, `cell_file_path`
|
|
(`backend/server.py` lines 124-137).
|
|
- Saved layout preview SVG: `database/<username>/layout/<project>/<cell>.svg`
|
|
- Path helper: `cell_svg_path` (`backend/server.py` lines 140-142).
|
|
- Optional route sidecar: `database/<username>/layout/<project>/<cell>.routes.yml`
|
|
- Path helper and writer: `cell_routes_path`, `write_route_points_sidecar`
|
|
(`backend/server.py` lines 145-175).
|
|
- Downloadable GDS export: `database/_exports/<uuid>/<project>.gds`
|
|
- Created by `create_export_path` (`backend/pdk_access.py` lines 53-59).
|
|
|
|
## Build Layout: Click Button -> YAML -> Router GDS -> SVG
|
|
|
|
Compact path:
|
|
|
|
`Click Build Layout` -> `handleBuildLayout` (`frontend/canvas.html` line 6209) :
|
|
`buildYamlForPage` (`frontend/canvas.html` line 6141) generates layout YAML ->
|
|
POST `/api/save-layout` (`frontend/canvas.html` line 6219) -> `save_layout`
|
|
(`backend/server.py` line 715) writes `<cell>.yml` -> `create_routed_layout_svg`
|
|
(`backend/server.py` line 734) -> `mxpic_router.build_project_gds` generates
|
|
temporary Nazca GDS -> `gdstk.read_gds` + `write_svg` writes `<cell>.svg` ->
|
|
backend returns `svg_url` -> `openLayoutPreview` (`frontend/canvas.html` line
|
|
6183) opens the SVG preview tab.
|
|
|
|
Detailed path:
|
|
|
|
1. The button is rendered only for non-preview pages:
|
|
- `<button onClick={handleBuildLayout}>` (`frontend/canvas.html` lines
|
|
6491-6498).
|
|
|
|
2. `handleBuildLayout` creates YAML for the active page:
|
|
- `handleBuildLayout` starts at `frontend/canvas.html` line 6209.
|
|
- It validates route crossings and calls `buildYamlForPage(activePage)` at
|
|
line 6215.
|
|
- `buildYamlForPage` starts at line 6141 and writes:
|
|
- `schema_version`, `kind`, `coordinate_system: gds_y_up`,
|
|
`canvas_size`, `project`, `name`, `type`, `version`.
|
|
- `pins` via `buildCanvasPinsYaml`.
|
|
- `instances` via `buildInstancesYaml`.
|
|
- `elements` via `buildElementsYaml`.
|
|
- `bundles` via `buildBundlesYaml`.
|
|
|
|
3. Frontend sends the YAML to Flask:
|
|
- `fetch('/api/save-layout', ...)` at `frontend/canvas.html` line 6219.
|
|
- Body includes `project`, `cell`, and `content` at lines 6222-6226.
|
|
|
|
4. Flask saves YAML and route sidecar:
|
|
- Route: `/api/save-layout` (`backend/server.py` line 713).
|
|
- Function: `save_layout` (`backend/server.py` line 715).
|
|
- `save_path = cell_file_path(project, cell)` (`backend/server.py` line 724).
|
|
- Writes the YAML content to disk (`backend/server.py` lines 727-728).
|
|
- Extracts route points into `<cell>.routes.yml` through
|
|
`write_route_points_sidecar` (`backend/server.py` line 729).
|
|
|
|
5. Flask always uses the router-backed SVG preview:
|
|
- Preview output path is computed with `cell_svg_path`
|
|
(`backend/server.py` line 733).
|
|
- `create_routed_layout_svg(...)` is called unconditionally for preview
|
|
generation (`backend/server.py` lines 734-742).
|
|
- There is no production branch based on `bundles.*.links`; no-link and
|
|
linked layouts both use the same router path.
|
|
|
|
6. Routed preview helper converts router GDS into SVG:
|
|
- `create_routed_layout_svg` starts at `backend/routed_layout_preview.py`
|
|
line 14.
|
|
- It loads the YAML string, gets the target cell name, and imports
|
|
`mxpic_router.build_project_gds` (`backend/routed_layout_preview.py` lines
|
|
25-32).
|
|
- It builds a temporary GDS with `target_cell_name=cell_name`
|
|
(`backend/routed_layout_preview.py` lines 37-46).
|
|
- It reads that temporary GDS with `gdstk.read_gds`
|
|
(`backend/routed_layout_preview.py` line 47).
|
|
- It writes the saved preview SVG with `top_cells[0].write_svg(output_path)`
|
|
(`backend/routed_layout_preview.py` line 51).
|
|
|
|
Inside sibling router checkout:
|
|
|
|
- `mxpic_router.build_project_gds` starts at
|
|
`../mxpic_router/mxpic_router/builder.py` line 12.
|
|
- `_load_project_specs` reads all saved `.yml` / `.yaml` files from the project
|
|
directory (`../mxpic_router/mxpic_router/builder.py` line 46).
|
|
- `load_cell_spec` reads YAML with `yaml.safe_load`
|
|
(`../mxpic_router/mxpic_router/eda_loader.py` line 80).
|
|
- `parse_cell_dict` converts YAML into `CellSpec`, including `pins`,
|
|
`elements`, `instances`, and `bundles`
|
|
(`../mxpic_router/mxpic_router/eda_loader.py` lines 85-157).
|
|
- `_build_cell` creates `with nd.Cell(name=spec.name) as top`
|
|
(`../mxpic_router/mxpic_router/builder.py` line 70).
|
|
- `_build_cell` places basic, local, or PDK GDS instances and registers pins
|
|
(`../mxpic_router/mxpic_router/builder.py` lines 73-108).
|
|
- `_route_link` draws routed geometry for each bundle link when links exist
|
|
(`../mxpic_router/mxpic_router/builder.py` line 269).
|
|
- `nd.export_gds(topcells=[built_cells[top.name]], filename=output_path)`
|
|
writes the temporary routed GDS (`../mxpic_router/mxpic_router/builder.py`
|
|
line 37).
|
|
|
|
## Build GDS: Click Button -> Saved YAML -> Router GDS
|
|
|
|
Compact path:
|
|
|
|
`Click Build GDS` -> Project Tree button calls `onBuildGds`
|
|
(`frontend/canvas.html` line 2738) -> `handleBuildGds`
|
|
(`frontend/canvas.html` line 6287) -> POST `/api/build-gds`
|
|
(`frontend/canvas.html` line 6294) -> `build_gds`
|
|
(`backend/server.py` line 775) -> `build_project_gds`
|
|
(`backend/gds_builder.py` line 26) : read saved `.yml` files for project
|
|
validation -> `_build_with_mxpic_router` (`backend/gds_builder.py` line 47) ->
|
|
`mxpic_router` creates `nd.Cell` objects and exports `.gds` -> backend returns
|
|
`download_url` -> frontend clicks a temporary download link.
|
|
|
|
Detailed path:
|
|
|
|
1. The `Build GDS` button is in the project tree header:
|
|
- `<button className="build-gds-btn" onClick={onBuildGds}>`
|
|
(`frontend/canvas.html` lines 2738-2739).
|
|
- `onBuildGds={handleBuildGds}` is passed into `LeftPanel`
|
|
(`frontend/canvas.html` line 6354).
|
|
|
|
2. `handleBuildGds` calls the backend:
|
|
- Function starts at `frontend/canvas.html` line 6287.
|
|
- It validates route crossings for all non-preview pages
|
|
(`frontend/canvas.html` lines 6289-6290).
|
|
- It POSTs only `{ project: currentProjectName }` to `/api/build-gds`
|
|
(`frontend/canvas.html` lines 6294-6298).
|
|
|
|
Important behavior:
|
|
|
|
- `Build GDS` does not serialize the current in-memory canvas first.
|
|
- It reads YAML files that already exist on disk.
|
|
- Those YAML files are produced by `Build Layout` or by the separate `Save`
|
|
button, where `handleSaveProjectLayouts` saves every non-preview page with
|
|
`preview: false` (`frontend/canvas.html` lines 6253-6284).
|
|
|
|
3. Flask creates a temporary export path and calls the GDS builder:
|
|
- Route: `/api/build-gds` (`backend/server.py` line 773).
|
|
- Function: `build_gds` (`backend/server.py` line 775).
|
|
- Old exports are cleaned (`backend/server.py` line 781).
|
|
- The project directory is resolved and validated
|
|
(`backend/server.py` lines 782-784).
|
|
- `create_export_path` creates `database/_exports/<uuid>/<project>.gds`
|
|
(`backend/server.py` line 785, `backend/pdk_access.py` lines 53-59).
|
|
- `build_project_gds(...)` is called (`backend/server.py` lines 786-792).
|
|
|
|
4. `backend/gds_builder.py` delegates to `mxpic_router`:
|
|
- `build_project_gds` starts at `backend/gds_builder.py` line 26.
|
|
- `_load_project_cells(project_dir)` reads every saved `.yml` / `.yaml`
|
|
file for validation (`backend/gds_builder.py` line 73).
|
|
- `_build_with_mxpic_router` starts at `backend/gds_builder.py` line 47.
|
|
- `_build_with_mxpic_router` calls `ensure_router_path` and imports
|
|
`mxpic_router.build_project_gds` (`backend/gds_builder.py` lines 54-55).
|
|
- There is no production fallback to local gdstk or local Nazca builders.
|
|
|
|
5. `mxpic_router` builds the final GDS:
|
|
- `mxpic_router.build_project_gds` starts at
|
|
`../mxpic_router/mxpic_router/builder.py` line 12.
|
|
- `_build_cell` creates one `nd.Cell` per `CellSpec`
|
|
(`../mxpic_router/mxpic_router/builder.py` line 70).
|
|
- `_resolve_pdk_asset` finds component YAML/GDS in the active PDK root
|
|
(`../mxpic_router/mxpic_router/builder.py` line 509).
|
|
- `_route_link` draws routed geometry when saved bundle links exist
|
|
(`../mxpic_router/mxpic_router/builder.py` line 269).
|
|
- `nd.export_gds(...)` writes the output GDS
|
|
(`../mxpic_router/mxpic_router/builder.py` line 37).
|
|
|
|
## PDK Asset Resolution
|
|
|
|
The active PDK root still comes from the EDA backend request/session state:
|
|
|
|
- `current_pdk_root` uses `pdk_root_for_session`
|
|
(`backend/server.py` lines 196-198).
|
|
- `pdk_root_for_session` chooses the root based on the logged-in user group
|
|
(`backend/pdk_access.py` lines 43-50).
|
|
- Preview and GDS generation pass that PDK root into `mxpic_router`.
|
|
- `mxpic_router._resolve_pdk_asset` resolves component YAML/GDS under that root
|
|
(`../mxpic_router/mxpic_router/builder.py` line 509).
|
|
|
|
## Download Completion
|
|
|
|
After GDS build:
|
|
|
|
`build_gds` returns `download_url` (`backend/server.py` line 803) ->
|
|
`handleBuildGds` creates a temporary `<a>` and clicks it
|
|
(`frontend/canvas.html` lines 6309-6316) -> `/api/exports/<export_id>/<filename>`
|
|
serves the GDS (`backend/server.py` lines 812-829) -> the temporary export
|
|
folder is deleted when the response closes (`backend/server.py` line 829).
|