Programm architecture simplified so that the EDA can now run individually without generation of .gds file

This commit is contained in:
2026-06-01 09:19:44 +08:00
parent 78f38d3be7
commit 7cf618fe02
160 changed files with 1640 additions and 11497 deletions
+73 -40
View File
@@ -11,14 +11,17 @@ const path = require('path');
const root = path.resolve(__dirname, '..');
const backendDir = path.join(root, 'backend');
const serverPy = fs.readFileSync(path.join(backendDir, 'server.py'), 'utf8');
const removedInternalPdkPath = ['mxpic', 'PDKs'].join('/');
const removedEdaPdkRootName = 'EDA_' + 'PDK_ROOT';
const removedYmlPathName = 'YML_' + 'PATH';
assert(
fs.existsSync(path.join(backendDir, 'layout_preview.py')),
'backend/layout_preview.py should generate SVG previews from saved layout YAML'
!fs.existsSync(path.join(backendDir, 'layout_preview.py')),
'EDA backend should not keep the legacy local GDS/SVG preview builder'
);
assert(
fs.existsSync(path.join(backendDir, 'pdk_registry.py')),
'backend/pdk_registry.py should resolve public PDK YAML/GDS assets'
!fs.existsSync(path.join(backendDir, 'pdk_registry.py')),
'EDA backend should not keep the legacy local PDK GDS registry'
);
assert(
fs.existsSync(path.join(backendDir, 'gds_builder.py')),
@@ -29,12 +32,23 @@ assert(
'backend/routed_layout_preview.py should create routed SVG previews through mxpic_router'
);
assert(
serverPy.includes('create_layout_svg_from_gds'),
'save-layout route should create a GDS-derived layout SVG preview'
fs.existsSync(path.join(backendDir, 'router_dependency.py')),
'backend/router_dependency.py should validate build-time mxpic_router runtime dependencies'
);
assert(
serverPy.includes('create_routed_layout_svg'),
'save-layout route should use routed preview generation when links exist'
!serverPy.includes('from router_dependency import require_router_stack') &&
!serverPy.includes('Verified mxpic_router stack') &&
!serverPy.includes('require_router_stack()'),
'server startup should not require mxpic_router; only build actions should validate the router stack'
);
assert(
!serverPy.includes('create_layout_svg_from_gds'),
'save-layout route should not use the legacy gdstk-only preview builder'
);
assert(
serverPy.includes('create_routed_layout_svg') &&
!serverPy.includes('if layout_has_links(content):'),
'save-layout route should always use routed preview generation through mxpic_router'
);
assert(
serverPy.includes('cell_routes_path') &&
@@ -46,6 +60,13 @@ assert(
serverPy.includes('svg_url'),
'save-layout response should include an svg_url for the new layout tab'
);
assert(
serverPy.includes('RouterStackUnavailable') &&
serverPy.includes('except RouterStackUnavailable as e') &&
serverPy.includes('"preview_status": preview_status') &&
serverPy.includes('"preview_error": preview_error'),
'save-layout should still return success and explain skipped SVG preview when mxpic_router is unavailable'
);
assert(
serverPy.includes("@app.route('/api/projects/<project_name>/cells/<cell_name>/layout.svg')"),
'server should expose a route for saved cell SVG previews'
@@ -63,49 +84,56 @@ assert(
'backend/technology_manifest.py should read generated technology manifests'
);
const techManifestPath = path.join(root, 'mxpic', 'PDKs', 'Silterra', 'EMO1_2ML_CU_Al_RDL', 'technology.yml');
assert(
fs.existsSync(techManifestPath),
'Silterra technology.yml should be generated into the EDA PDK folder'
);
const techManifest = fs.readFileSync(techManifestPath, 'utf8');
for (const xsection of ['strip', 'rib_low', 'metal_1', 'metal_2']) {
assert(techManifest.includes(`${xsection}:`), `technology.yml should include ${xsection}`);
}
assert(techManifest.includes('family: optical'), 'technology.yml should classify optical xsections');
assert(techManifest.includes('family: electrical'), 'technology.yml should classify electrical xsections');
const layoutPreviewPy = fs.readFileSync(path.join(backendDir, 'layout_preview.py'), 'utf8');
assert(
layoutPreviewPy.includes('read_gds') || layoutPreviewPy.includes('load_gds'),
'layout_preview.py should load public _BB.gds geometry, not draw only schematic boxes'
!serverPy.includes(removedEdaPdkRootName) &&
!serverPy.includes(removedYmlPathName) &&
!serverPy.includes("'mxpic', 'PDKs'") &&
!serverPy.includes(removedInternalPdkPath),
'server should not use an internal EDA PDK copy as a separate technology source of truth'
);
assert(
layoutPreviewPy.includes('_BB.gds') || layoutPreviewPy.includes('gds_path'),
'layout_preview.py should resolve public GDS assets for placed components'
serverPy.includes('pdks_root = current_pdk_root()') &&
serverPy.includes('os.path.exists(os.path.join(technology_path, "technology.yml"))'),
'technology list should scan the active role PDK root and only expose folders containing technology.yml'
);
assert(
serverPy.includes('read_technology_manifest(current_pdk_root()'),
'technology manifest API should read technology.yml from the active role PDK root'
);
assert(
!fs.existsSync(path.join(root, 'mxpic')),
'mxpic_EDA should not contain a redundant internal technology copy'
);
const gdsBuilderPy = fs.readFileSync(path.join(backendDir, 'gds_builder.py'), 'utf8');
assert(
gdsBuilderPy.includes('_cells_have_links') && gdsBuilderPy.includes('Routed Build GDS requires mxpic_router'),
'Build GDS should not silently fall back to unrouted gdstk when links are present'
gdsBuilderPy.includes('return _build_with_mxpic_router(') &&
!gdsBuilderPy.includes('return _build_with_gdstk') &&
!gdsBuilderPy.includes('return _build_with_nazca'),
'Build GDS should use mxpic_router as the only production builder'
);
assert(
gdsBuilderPy.includes('_cells_have_elements') &&
gdsBuilderPy.includes('Build GDS with Port/Anchor elements requires Nazca') &&
gdsBuilderPy.indexOf('if _cells_have_elements(cells):') < gdsBuilderPy.indexOf('return _build_with_gdstk'),
'Build GDS should route layouts with Port/Anchor elements through Nazca so element pins survive as cell metadata'
gdsBuilderPy.includes('from router_dependency import require_router_stack') &&
gdsBuilderPy.includes('require_router_stack()'),
'Build GDS API wrapper should validate mxpic_router only when a GDS build is requested'
);
const routedPreviewPy = fs.readFileSync(path.join(backendDir, 'routed_layout_preview.py'), 'utf8');
assert(
gdsBuilderPy.includes('_build_nazca_element_cells') &&
gdsBuilderPy.includes('_build_nazca_element_cell') &&
gdsBuilderPy.includes('element.get("pin_number"') &&
gdsBuilderPy.includes('_element_pin_names') &&
gdsBuilderPy.includes('nd.Pin(pin_name, width=width).put(0.0, y, 180.0)') &&
gdsBuilderPy.includes('nd.Pin(anchor_pin_names[index * 2], width=width).put(0.0, y, 180.0)') &&
gdsBuilderPy.includes('nd.Pin(anchor_pin_names[index * 2 + 1], width=width).put(0.0, y, 0.0)') &&
gdsBuilderPy.includes('element_cell.put(x, y, rotation)'),
'Nazca fallback should model Port and Anchor objects as placed element cells with named local nd.Pin definitions'
routedPreviewPy.includes('from router_dependency import require_router_stack') &&
routedPreviewPy.includes('require_router_stack(require_gdstk=True)'),
'Build Layout preview should validate mxpic_router and gdstk only when preview generation is requested'
);
const routerDependencyPy = fs.readFileSync(path.join(backendDir, 'router_dependency.py'), 'utf8');
assert(
routerDependencyPy.includes('def require_router_stack') &&
routerDependencyPy.includes('class RouterStackUnavailable') &&
routerDependencyPy.includes('require_gdstk: bool = False') &&
routerDependencyPy.includes('importlib.import_module("nazca")') &&
routerDependencyPy.includes('mxpic_router.builder') &&
routerDependencyPy.includes('_import_mxpic_forge_route'),
'router dependency gate should validate mxpic_router, Nazca, optional gdstk, and route backend imports'
);
const routerDir = path.resolve(root, '..', 'mxpic_router', 'mxpic_router');
@@ -144,3 +172,8 @@ assert(
serverPy.includes('os.path.relpath(root, path_root)'),
'library tree leaves should preserve component paths relative to the role PDK root'
);
assert(
serverPy.includes('f.lower() != "technology.yml"') &&
serverPy.includes('component_yml_files'),
'component library scanning should ignore technology.yml so it can descend into technology folders'
);
+5
View File
@@ -32,6 +32,11 @@ assert(
canvasHtml.includes('svg_url'),
'Build Layout should use the backend svg_url response'
);
assert(
canvasHtml.includes('result.preview_error') &&
canvasHtml.includes('Preview skipped: '),
'Build Layout should log when the backend saves YAML but skips SVG preview because the router stack is unavailable'
);
assert(
canvasHtml.includes('layoutPreview'),
'canvas pages should support a layoutPreview tab type'