Files
mxpic_EDA/docs/GDS_SVG_GENERATION_LOGIC.md
2026-06-04 17:03:04 +08:00

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 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).
  1. 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).
  2. 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.
  3. 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).