10 KiB
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 LayoutSVG preview ->backend/routed_layout_preview.py->require_router_stack(require_gdstk=True).
Important functions:
ensure_router_path(backend/router_dependency.pyline 23) adds the sibling../mxpic_routercheckout tosys.pathwhen present.require_router_stack(backend/router_dependency.pyline 31) importsmxpic_router,nazca, and the route backend used bymxpic_router. The preferred route backend ismxpic_forge.Route; if it is absent,mxpic_routerfalls back to Nazcainterconnects.Interconnect.gdstkis 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.pylines 124-137).
- Path helpers:
- Saved layout preview SVG:
database/<username>/layout/<project>/<cell>.svg- Path helper:
cell_svg_path(backend/server.pylines 140-142).
- Path helper:
- Optional route sidecar:
database/<username>/layout/<project>/<cell>.routes.yml- Path helper and writer:
cell_routes_path,write_route_points_sidecar(backend/server.pylines 145-175).
- Path helper and writer:
- Downloadable GDS export:
database/_exports/<uuid>/<project>.gds- Created by
create_export_path(backend/pdk_access.pylines 53-59).
- Created by
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:
-
The button is rendered only for non-preview pages:
<button onClick={handleBuildLayout}>(frontend/canvas.htmllines 6491-6498).
-
handleBuildLayoutcreates YAML for the active page:handleBuildLayoutstarts atfrontend/canvas.htmlline 6209.- It validates route crossings and calls
buildYamlForPage(activePage)at line 6215. buildYamlForPagestarts at line 6141 and writes:schema_version,kind,coordinate_system: gds_y_up,canvas_size,project,name,type,version.pinsviabuildCanvasPinsYaml.instancesviabuildInstancesYaml.elementsviabuildElementsYaml.bundlesviabuildBundlesYaml.
-
Frontend sends the YAML to Flask:
fetch('/api/save-layout', ...)atfrontend/canvas.htmlline 6219.- Body includes
project,cell, andcontentat lines 6222-6226.
-
Flask saves YAML and route sidecar:
- Route:
/api/save-layout(backend/server.pyline 713). - Function:
save_layout(backend/server.pyline 715). save_path = cell_file_path(project, cell)(backend/server.pyline 724).- Writes the YAML content to disk (
backend/server.pylines 727-728). - Extracts route points into
<cell>.routes.ymlthroughwrite_route_points_sidecar(backend/server.pyline 729).
- Route:
-
Flask always uses the router-backed SVG preview:
- Preview output path is computed with
cell_svg_path(backend/server.pyline 733). create_routed_layout_svg(...)is called unconditionally for preview generation (backend/server.pylines 734-742).- There is no production branch based on
bundles.*.links; no-link and linked layouts both use the same router path.
- Preview output path is computed with
-
Routed preview helper converts router GDS into SVG:
create_routed_layout_svgstarts atbackend/routed_layout_preview.pyline 14.- It loads the YAML string, gets the target cell name, and imports
mxpic_router.build_project_gds(backend/routed_layout_preview.pylines 25-32). - It builds a temporary GDS with
target_cell_name=cell_name(backend/routed_layout_preview.pylines 37-46). - It reads that temporary GDS with
gdstk.read_gds(backend/routed_layout_preview.pyline 47). - It writes the saved preview SVG with
top_cells[0].write_svg(output_path)(backend/routed_layout_preview.pyline 51).
Inside sibling router checkout:
mxpic_router.build_project_gdsstarts at../mxpic_router/mxpic_router/builder.pyline 12._load_project_specsreads all saved.yml/.yamlfiles from the project directory (../mxpic_router/mxpic_router/builder.pyline 46).load_cell_specreads YAML withyaml.safe_load(../mxpic_router/mxpic_router/eda_loader.pyline 80).parse_cell_dictconverts YAML intoCellSpec, includingpins,elements,instances, andbundles(../mxpic_router/mxpic_router/eda_loader.pylines 85-157)._build_cellcreateswith nd.Cell(name=spec.name) as top(../mxpic_router/mxpic_router/builder.pyline 70)._build_cellplaces basic, local, or PDK GDS instances and registers pins (../mxpic_router/mxpic_router/builder.pylines 73-108)._route_linkdraws routed geometry for each bundle link when links exist (../mxpic_router/mxpic_router/builder.pyline 269).nd.export_gds(topcells=[built_cells[top.name]], filename=output_path)writes the temporary routed GDS (../mxpic_router/mxpic_router/builder.pyline 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:
-
The
Build GDSbutton is in the project tree header:<button className="build-gds-btn" onClick={onBuildGds}>(frontend/canvas.htmllines 2738-2739).onBuildGds={handleBuildGds}is passed intoLeftPanel(frontend/canvas.htmlline 6354).
-
handleBuildGdscalls the backend:- Function starts at
frontend/canvas.htmlline 6287. - It validates route crossings for all non-preview pages
(
frontend/canvas.htmllines 6289-6290). - It POSTs only
{ project: currentProjectName }to/api/build-gds(frontend/canvas.htmllines 6294-6298).
- Function starts at
Important behavior:
Build GDSdoes not serialize the current in-memory canvas first.- It reads YAML files that already exist on disk.
- Those YAML files are produced by
Build Layoutor by the separateSavebutton, wherehandleSaveProjectLayoutssaves every non-preview page withpreview: false(frontend/canvas.htmllines 6253-6284).
-
Flask creates a temporary export path and calls the GDS builder:
- Route:
/api/build-gds(backend/server.pyline 773). - Function:
build_gds(backend/server.pyline 775). - Old exports are cleaned (
backend/server.pyline 781). - The project directory is resolved and validated
(
backend/server.pylines 782-784). create_export_pathcreatesdatabase/_exports/<uuid>/<project>.gds(backend/server.pyline 785,backend/pdk_access.pylines 53-59).build_project_gds(...)is called (backend/server.pylines 786-792).
- Route:
-
backend/gds_builder.pydelegates tomxpic_router:build_project_gdsstarts atbackend/gds_builder.pyline 26._load_project_cells(project_dir)reads every saved.yml/.yamlfile for validation (backend/gds_builder.pyline 73)._build_with_mxpic_routerstarts atbackend/gds_builder.pyline 47._build_with_mxpic_routercallsensure_router_pathand importsmxpic_router.build_project_gds(backend/gds_builder.pylines 54-55).- There is no production fallback to local gdstk or local Nazca builders.
-
mxpic_routerbuilds the final GDS:mxpic_router.build_project_gdsstarts at../mxpic_router/mxpic_router/builder.pyline 12._build_cellcreates onend.CellperCellSpec(../mxpic_router/mxpic_router/builder.pyline 70)._resolve_pdk_assetfinds component YAML/GDS in the active PDK root (../mxpic_router/mxpic_router/builder.pyline 509)._route_linkdraws routed geometry when saved bundle links exist (../mxpic_router/mxpic_router/builder.pyline 269).nd.export_gds(...)writes the output GDS (../mxpic_router/mxpic_router/builder.pyline 37).
PDK Asset Resolution
The active PDK root still comes from the EDA backend request/session state:
current_pdk_rootusespdk_root_for_session(backend/server.pylines 196-198).pdk_root_for_sessionchooses the root based on the logged-in user group (backend/pdk_access.pylines 43-50).- Preview and GDS generation pass that PDK root into
mxpic_router. mxpic_router._resolve_pdk_assetresolves component YAML/GDS under that root (../mxpic_router/mxpic_router/builder.pyline 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).