20 KiB
PDK, Technology, and Xsection Loading Logic Path
This document explains how the current code loads:
- PDK component information: component folders, component YAML, images, and GDS assets.
- Technology information: selected foundry/technology for each project.
- Xsection information: routing cross-section defaults and Nazca layer bindings from
technology.yml.
Line numbers refer to the current codebase at the time this document was written.
Mental Model
There are three related but separate data paths.
-
Component PDK assets
- Source: role-controlled PDK trees:
opt_pdk_public/foundriesopt_pdk_atlas/foundries
- Controlled by user group through
pdk_root_for_session(backend/pdk_access.pyline 43). - Used for library browsing, component metadata, component images, and final component GDS resolution.
- Source: role-controlled PDK trees:
-
Technology registry and manifests
- Source: the same role-controlled PDK trees as component assets.
- Path:
<active_role_pdk_root>/<foundry>/<technology>/technology.yml. - Used to list available technologies, save the project technology choice, load route defaults, and register Nazca layers/xsections.
-
Saved project layout YAML
- Source:
mxpic_EDA_database/<username>/layout/<project>/<cell>.yml. - Contains placed component paths and
bundles.*.links[*].xsection. - Consumed by
mxpic_routerwhen building SVG preview GDS or downloadable GDS.
- Source:
Important distinction:
- The active role PDK root is now the single source of truth.
- Normal/developer users read from
opt_pdk_public/foundries. - Manager users read from
opt_pdk_atlas/foundries. - The selected project technology scopes the library browser and selects which
technology.ymlis loaded from that same role root.
Active Roots
Backend startup defines the important paths:
REPO_ROOT(backend/server.pyline 36) points two levels abovebackend/.- There is no separate technology-manifest copy inside
mxpic_EDA.
Role-based PDK root:
Flask session user_group -> current_pdk_root
(backend/server.py line 196) -> pdk_root_for_session
(backend/pdk_access.py line 43) -> pdk_root_for_group
(backend/pdk_access.py line 27) : choose:
- manager ->
MXPIC_PDK_ATLAS_ROOTor../opt_pdk_atlas/foundries. - developers/user ->
MXPIC_PDK_PUBLIC_ROOTor../opt_pdk_public/foundries.
Technology Selection Path
Compact path:
Dashboard opens -> loadTechnologies (frontend/dashboard.html line 1238)
-> GET /api/technologies -> list_technologies
(backend/server.py line 441) : scan current_pdk_root() ->
return { foundry, technology, id, label } -> dashboard fills the technology
select.
Detailed path:
-
The dashboard calls
/api/technologies:loadTechnologiesstarts atfrontend/dashboard.htmlline 1238.- It stores
data.technologiesand creates<option>values usingtech.id, for exampleSilterra/EMO1_2ML_CU_Al_RDL.
-
The backend scans the technology registry:
- Route:
/api/technologies(backend/server.pyline 439). - Function:
list_technologies(backend/server.pyline 441). - It scans
current_pdk_root(). - A technology directory is exposed only when it contains
technology.yml. - For each valid directory pair it returns:
foundrytechnologyid: foundry/technologylabel: foundry / technology
- Route:
-
Creating a project stores the selected technology:
- Dashboard create click posts to
/api/projects(frontend/dashboard.htmlline 1303). - Body contains
{ name, technology }. - Backend route:
/api/projects(backend/server.pyline 603). - Function:
create_project(backend/server.pyline 605). - It writes
.project.jsonwith:nametechnology
- Metadata helper path:
project_meta_path(backend/server.pyline 228). - Metadata writer:
write_project_meta(backend/server.pyline 242).
- Dashboard create click posts to
Saved metadata location:
mxpic_EDA_database/<username>/layout/<project>/.project.json
Example:
{
"name": "mxpic_project_1",
"technology": "Silterra/EMO1_2ML_CU_Al_RDL"
}
Technology Manifest Loading Path
Compact path:
Canvas opens project -> loadProject (frontend/canvas.html line 5030)
-> GET /api/projects/<project> -> get_project
(backend/server.py line 632) : returns saved technology -> frontend
loadTechnologyManifest (frontend/canvas.html line 4056) -> GET
/api/technologies/<foundry>/<technology>/manifest -> read_technology_manifest
(backend/technology_manifest.py line 27) -> return full technology.yml
manifest.
Detailed path:
-
Canvas loads project metadata:
loadProjectfetches/api/projects/<project>(frontend/canvas.htmlline 5033).- Backend
get_projectreturns:- project name
- saved cells
technology: read_project_meta(project_name).get("technology")(backend/server.pyline 653).
-
Canvas loads the selected technology manifest:
loadTechnologyManifeststarts atfrontend/canvas.htmlline 4056.- It expects technology IDs in
foundry/technologyformat. - It fetches:
/api/technologies/<foundry>/<technology>/manifest. - If missing or invalid, it logs an error and uses
FALLBACK_TECHNOLOGY_MANIFEST.
-
Backend validates and returns the manifest:
- Route:
/api/technologies/<foundry>/<technology>/manifest(backend/server.pyline 465). - Function:
get_technology_manifest(backend/server.pyline 467). - It calls
read_technology_manifest(backend/technology_manifest.pyline 27). - The expected path is built by
technology_manifest_path(backend/technology_manifest.pyline 16):
- Route:
<active_role_pdk_root>/<foundry>/<technology>/technology.yml
- Backend manifest validation:
technology.ymlmust exist.manifest.xsectionsmust be a dictionary.manifest.defaultsmust be a dictionary.- Otherwise the API returns 404 with a
TechnologyManifestError.
Current technology.yml Structure
Active example:
opt_pdk_public/foundries/Silterra/EMO1_2ML_CU_Al_RDL/technology.yml
For manager sessions, the equivalent file is read from
opt_pdk_atlas/foundries.
Important sections:
schema_version(technology.ymlline 7).foundry(technology.ymlline 8).technology(technology.ymlline 9).layers(technology.ymlline 18).routing_types(technology.ymlline 41).defaults(technology.ymlline 44).xsections(technology.ymlline 50).
Current xsections:
strip(technology.ymlline 51).rib_low(technology.ymlline 58).metal_1(technology.ymlline 67).metal_2(technology.ymlline 74).
Meaning of the main fields:
layers: maps technology layer names to GDS layer/datatype pairs.routing_types: choices shown by the route editor.defaults: default routexsection,family,width,radius, androuting_type.xsections: per-cross-section defaults and layer bindings.constantsandsource_class: currently kept as manifest metadata. The active frontend and router code do not consume them directly.
Frontend Xsection Usage
Compact path:
technology.yml xsections -> backend manifest API -> technologyManifest
state (frontend/canvas.html line 3756) -> route editor dropdown and route
defaults -> saved YAML bundles.output_bus.links[*].xsection.
Detailed path:
-
Fallback manifest:
FALLBACK_TECHNOLOGY_MANIFESTis defined infrontend/canvas-helpers.jsline 105.- It includes fallback
strip,rib_low,metal_1, andmetal_2xsections.
-
Canvas state:
projectTechnologystate is declared atfrontend/canvas.htmlline 3755.technologyManifeststate is declared atfrontend/canvas.htmlline 3756.
-
Route defaults:
getXsectionInfo(frontend/canvas-helpers.jsline 132) looks upmanifest.xsections[xsection].createRouteSettings(frontend/canvas-helpers.jsline 138) merges:- manifest defaults
- selected xsection defaults
- user overrides saved on the edge
- Resulting route state always has:
xsectionfamilywidthradiusrouting_type
-
Changing xsection in the route editor:
- Right panel builds available xsections from
Object.keys(technologyManifest.xsections)(frontend/canvas.htmlline 2999). - The xsection select calls
updateRouteXsection(frontend/canvas.htmlline 3037). updateRouteXsectionrefreshes family, default width, and default radius (frontend/canvas-helpers.jsline 167).
- Right panel builds available xsections from
-
Link toolbar choices:
linkXsectionChoicesis built fromtechnologyManifest.xsections(frontend/canvas.htmlline 3775).- It prefers the order
strip,rib_low,metal_1,metal_2, then any extra xsections.
Component PDK Library Loading Path
Compact path:
Canvas needs library -> fetchLibrary (frontend/canvas.html line 4576)
-> GET /api/library?project=<project> -> pdk_root_for_request_project
(backend/server.py line 222) -> scoped_pdk_root_for_project
(backend/server.py line 201) -> findComps
(backend/server.py line 262) -> frontend library tree.
Detailed path:
-
Frontend requests the library:
fetchLibrarycalls/api/library?project=<currentProjectName>(frontend/canvas.htmlline 4576).
-
Backend chooses the PDK root for this request:
- Route:
/api/library(backend/server.pyline 854). - Function:
getLibusespdk_root_for_request_project. pdk_root_for_request_projectstarts atbackend/server.pyline 222.- If a project is supplied, it calls
scoped_pdk_root_for_project. - Otherwise it uses
current_pdk_root.
- Route:
-
Project scoping:
scoped_pdk_root_for_projectstarts atbackend/server.pyline 201.- It reads the project
.project.jsontechnology field. - It splits the value into
foundry/technology. - It scopes the role root to:
<active_role_pdk_root>/<foundry>/<technology>
- It only returns that scoped path if it exists and stays under the active role root.
- If not valid, it falls back to the active role root.
- Component scanning:
findCompsstarts atbackend/server.pyline 262.- It walks the chosen PDK directory.
- Any folder containing a
.ymlfile is treated as one component leaf. - It stores component metadata:
- folder path
- first component YAML filename
- category
- relative component path
addCompsToTree(backend/server.pyline 290) converts this flat map into the nested library tree returned to the canvas.
Important detail:
getLib scans the project-scoped directory but calls:
findComps(comps_root, current_pdk_root())
This means the visible library is limited to the selected project technology,
but each component __path__ is stored relative to the base role PDK root, not
relative to the scoped technology directory.
For example, if the active role PDK root is:
opt_pdk_public/foundries
and the project technology is:
Silterra/EMO1_2ML_CU_Al_RDL
a component path saved into layout YAML can include:
Silterra/EMO1_2ML_CU_Al_RDL/<category>/<component>/<component>
This is important because build and preview pass the base role PDK root into
mxpic_router, and the saved component path can still resolve under that base
root.
Component Metadata and Image Loading
Component YAML path:
Canvas load/drop component -> loadComponentMetadata
(frontend/canvas.html line 4082) -> GET
/api/component/<component_name>?project=<project> -> readCompYaml
(backend/server.py line 345) -> return component YAML.
Details:
- Route:
/api/component/<component_name>(backend/server.pyline 869). - The backend uses the same project-scoped PDK root logic as
/api/library. readCompYamlwalks the selected PDK root and matches the component folder basename.- If the PDK root is under
opt_pdk_publicoropt_pdk_atlas, legacy component metadata withportsis normalized topins.
Component image path:
Canvas thumbnail -> /api/component/<component_name>/image?project=<project>
-> find_component_dir (backend/server.py line 367) -> first
.png, .jpg, .jpeg, or .svg in the component folder.
Route:
/api/component/<component_name>/image(backend/server.pyline 878).
Saved YAML: How PDK Paths and Xsections Are Written
Compact path:
Build Layout or Save -> buildYamlForPage
(frontend/canvas.html line 6141) -> findComponentPath
(frontend/canvas.html line 3659) -> buildInstancesYaml
(frontend/canvas-helpers.js line 665) -> saved instances.*.component.
Component path details:
findComponentPathsearches the loaded library tree.- If a component node has
__path__, that path is used. buildInstanceYaml(frontend/canvas-helpers.jsline 643) writes:
instances:
<instance_name>:
component: <component path from library>
- Forge and basic components are special:
- Forge components save the forge component type.
- Basic components save names such as
waveguide,90 bend, ortaper. - Normal PDK components save the PDK library path.
Xsection path details:
Canvas edges -> buildBundlesYaml (frontend/canvas-helpers.js line 988)
-> createRouteSettings (frontend/canvas-helpers.js line 138) -> saved
bundle links:
bundles:
output_bus:
routing_type: euler_bend
links:
- from: <instance>:<pin>
to: <instance>:<pin>
xsection: strip
family: optical
width: 0.45
radius: 10
routing_type: euler_bend
The saved xsection field is the bridge from frontend route editing into
mxpic_router and Nazca.
Build-Time Technology and PDK Loading
Both SVG preview and final GDS now use the router-backed path.
SVG Preview
Compact path:
POST /api/save-layout -> save_layout (backend/server.py line 715)
-> create_routed_layout_svg (backend/routed_layout_preview.py line 14)
-> mxpic_router.build_project_gds -> temporary GDS -> gdstk.write_svg.
Arguments passed from save_layout:
pdk_root=current_pdk_root()(backend/server.pyline 737).technology_manifest_path=technology_manifest_path_for_project(project)(backend/server.pyline 739).prefer_full_gds=prefer_full_gds_for_session(session)(backend/server.pyline 740).
The preview helper:
- Imports
mxpic_router.build_project_gds(backend/routed_layout_preview.pyline 32). - Builds a temporary GDS with the same PDK root and technology manifest path
(
backend/routed_layout_preview.pylines 39-45). - Reads that GDS with
gdstk. - Writes the SVG preview (
backend/routed_layout_preview.pyline 51).
Final GDS
Compact path:
POST /api/build-gds -> build_gds (backend/server.py line 775)
-> backend.gds_builder.build_project_gds (backend/gds_builder.py line 26)
-> _build_with_mxpic_router (backend/gds_builder.py line 47)
-> mxpic_router.build_project_gds.
Arguments passed from build_gds:
current_pdk_root()(backend/server.pyline 789).technology_manifest_path=technology_manifest_path_for_project(project)(backend/server.pyline 790).prefer_full_gds=prefer_full_gds_for_session(session)(backend/server.pyline 791).
technology_manifest_path_for_project:
- Starts at
backend/server.pyline 183. - Reads the project technology from
.project.json. - Builds:
<active_role_pdk_root>/<foundry>/<technology>/technology.yml
- Returns the path only if it exists and stays under
current_pdk_root().
Inside mxpic_router
Compact path:
mxpic_router.build_project_gds (../mxpic_router/mxpic_router/builder.py
line 12) -> load_technology_manifest
(../mxpic_router/mxpic_router/technology.py line 6) ->
apply_technology_manifest (../mxpic_router/mxpic_router/technology.py
line 13) -> Nazca nd.add_layer, nd.add_xsection,
nd.add_layer2xsection.
Detailed path:
-
Build starts:
build_project_gdsstarts at../mxpic_router/mxpic_router/builder.pyline 12.- It imports Nazca as
nd. - It loads the manifest from
technology_manifest_path(../mxpic_router/mxpic_router/builder.pyline 23). - It applies the manifest to Nazca
(
../mxpic_router/mxpic_router/builder.pyline 24).
-
Technology manifest to Nazca:
load_technology_manifestreturns{}if the path is missing (../mxpic_router/mxpic_router/technology.pyline 6).apply_technology_manifestdoes nothing for an empty manifest.- For each
manifest.layersentry it calls:
nd.add_layer(name=name, layer=(layer, datatype), overwrite=True)
- For each
manifest.xsectionsentry it calls:
nd.add_xsection(name=xsection)
nd.add_layer2xsection(...)
- Supported layer binding keys are:
growxgrowyleftedgerightedge
-
Project YAML parsing:
_load_project_specsreads saved.yml/.yamlfiles (../mxpic_router/mxpic_router/builder.pyline 46).load_cell_specreads YAML (../mxpic_router/mxpic_router/eda_loader.pyline 80).parse_cell_dictconverts dictionaries intoCellSpec(../mxpic_router/mxpic_router/eda_loader.pyline 85).
-
Link xsections:
LinkSpec.xsectiondefaults tostrip(../mxpic_router/mxpic_router/eda_loader.pyline 51).BundleSpec.xsectiondefaults tostrip(../mxpic_router/mxpic_router/eda_loader.pyline 66).- Bundle-level
xsectionis read at../mxpic_router/mxpic_router/eda_loader.pyline 137. - Link-level
xsectionoverrides bundle-level xsection at../mxpic_router/mxpic_router/eda_loader.pyline 142. - If
familyis missing,_family_from_xsectionmaps metal-like xsections toelectrical, otherwiseoptical(../mxpic_router/mxpic_router/eda_loader.pyline 171).
-
PDK asset resolution:
_resolve_pdk_assetstarts at../mxpic_router/mxpic_router/builder.pyline 509.- It first tries the saved component path directly under
pdk_root. - If no direct directory exists, it scans the whole PDK root by component folder basename.
- It reads the component YAML if present.
- It chooses GDS through
_find_gds(../mxpic_router/mxpic_router/builder.pyline 533). - Normal users prefer
<component>_BB.gds; manager sessions can prefer<component>.gds.
-
Route generation:
_route_linkstarts at../mxpic_router/mxpic_router/builder.pyline 269.- It creates the route object with:
Route(radius=link.radius or 10, width=link.width, xs=link.xsection, PCB=...)
- Optical and electrical routes both use
link.xsection; metal routes also enable PCB-style routing formetal_1andmetal_2. - Nazca can generate geometry on the correct layers because
apply_technology_manifestalready registered the xsection layer bindings.
Current Fallbacks and Observations
- If a project has no saved technology, the frontend uses
FALLBACK_TECHNOLOGY_MANIFEST. - If
/api/technologies/<foundry>/<technology>/manifestfails, the frontend also usesFALLBACK_TECHNOLOGY_MANIFEST. - If build-time
technology_manifest_path_for_projectcannot findtechnology.yml,mxpic_routerreceives no manifest path and Nazca registration is skipped. - The library browser is project-scoped by selected technology, but preview and
GDS build pass the base role PDK root to
mxpic_router. - This works because normal saved component paths include
foundry/technology/...relative to the base role PDK root. - Manually edited YAML with only a component folder name can still resolve, but the router will scan the whole active PDK root and use the first matching folder basename.
- The legacy local GDS/SVG preview builder and local PDK GDS registry have been
removed from
mxpic_EDA; active build and preview actions call the router-backed API wrappers instead.