@hybridlabor-api/bdb-hardware-pcb 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +116 -0
- package/config/mcp/antigravity.json +22 -0
- package/config/mcp/claude.json +22 -0
- package/config/mcp/codex.toml +25 -0
- package/docs/adr/ADR-001-KICAD-OPENSCAD-MCP-STRATEGY.md +334 -0
- package/docs/review_documentation.md +121 -0
- package/installer.js +358 -0
- package/mcp_servers/kicad-mcp-server/.env.example +22 -0
- package/mcp_servers/kicad-mcp-server/.github/workflows/ci.yml +36 -0
- package/mcp_servers/kicad-mcp-server/CLAUDE.md +487 -0
- package/mcp_servers/kicad-mcp-server/README.md +316 -0
- package/mcp_servers/kicad-mcp-server/docs/DEVICE_TREE.md +416 -0
- package/mcp_servers/kicad-mcp-server/docs/INSTALLATION.md +332 -0
- package/mcp_servers/kicad-mcp-server/docs/PIN_ANALYSIS.md +332 -0
- package/mcp_servers/kicad-mcp-server/docs/README.md +240 -0
- package/mcp_servers/kicad-mcp-server/docs/TESTING.md +613 -0
- package/mcp_servers/kicad-mcp-server/docs/VALIDATION.md +268 -0
- package/mcp_servers/kicad-mcp-server/pyproject.toml +96 -0
- package/mcp_servers/kicad-mcp-server/requirements-dev.txt +16 -0
- package/mcp_servers/kicad-mcp-server/requirements-test.txt +24 -0
- package/mcp_servers/kicad-mcp-server/requirements.txt +13 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/__init__.py +3 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/__main__.py +17 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/config.py +46 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/models/__init__.py +1 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/models/types.py +87 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/parsers/__init__.py +1 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/parsers/netlist_parser.py +234 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/parsers/pcb_parser.py +375 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/parsers/pcb_parser_kicad.py +327 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/parsers/schematic_parser.py +902 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/server.py +71 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/__init__.py +1 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/arduino/connectivity_test.cpp.j2 +189 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/device_tree/atmega.dts.j2 +77 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/device_tree/esp32.dts.j2 +77 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/device_tree/nrf52.dts.j2 +77 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/device_tree/stm32f4.dts.j2 +89 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/esp_idf/test_suite.c.j2 +340 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/pytest/test_connectivity.py.j2 +147 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/st_hal/hal_test.c.j2 +313 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/tests/pytest_gpio_test.py.j2 +99 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/tests/pytest_i2c_test.py.j2 +117 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/tests/pytest_pinmux_test.py.j2 +43 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/tests/pytest_spi_test.py.j2 +94 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/tests/unity_gpio_test.c.j2 +113 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/tests/unity_i2c_test.c.j2 +101 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/tests/unity_spi_test.c.j2 +94 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/unittest/test_schematic.py.j2 +172 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/__init__.py +36 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/device_tree.py +1187 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/hierarchical_analysis.py +211 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/netlist.py +320 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/parts_registry.py +142 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/pcb.py +955 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/pcb_layout.py +308 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/pin_analysis.py +765 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/project.py +196 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/schematic.py +319 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/schematic_editor.py +674 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/schematic_search.py +158 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/validation.py +866 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/visualization.py +225 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/utils/__init__.py +1 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/utils/file_handlers.py +65 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/utils/kicad_cli.py +103 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/utils/kicad_version.py +103 -0
- package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/utils/parts_registry.py +197 -0
- package/mcp_servers/kicad-mcp-server/tests/__init__.py +1 -0
- package/mcp_servers/kicad-mcp-server/tests/examples/ESP32S3_TEST.md +219 -0
- package/mcp_servers/kicad-mcp-server/tests/fixtures/README.md +65 -0
- package/mcp_servers/kicad-mcp-server/tests/fixtures/__init__.py +1 -0
- package/mcp_servers/kicad-mcp-server/tests/fixtures/example_pcb.kicad_pcb +177 -0
- package/mcp_servers/kicad-mcp-server/tests/fixtures/example_schematic.kicad_sch +145 -0
- package/mcp_servers/kicad-mcp-server/tests/fixtures/hier/child.kicad_sch +24 -0
- package/mcp_servers/kicad-mcp-server/tests/fixtures/hier/root.kicad_sch +38 -0
- package/mcp_servers/kicad-mcp-server/tests/test_tools/__init__.py +1 -0
- package/mcp_servers/kicad-mcp-server/tests/test_tools/test_hierarchical_labels.py +195 -0
- package/mcp_servers/kicad-mcp-server/tests/test_tools/test_kicad_cli.py +116 -0
- package/mcp_servers/kicad-mcp-server/tests/test_tools/test_netlist_cache_path.py +29 -0
- package/mcp_servers/kicad-mcp-server/tests/test_tools/test_schematic.py +189 -0
- package/mcp_servers/kicad-mcp-server/tests/test_tools/test_schematic_hierarchy.py +69 -0
- package/mcp_servers/kicad-mcp-server/tests/test_tools/test_visualization.py +136 -0
- package/mcp_servers/kicad-mcp-server/uv.lock +2873 -0
- package/mcp_servers/openscad-mcp-server/.dockerignore +9 -0
- package/mcp_servers/openscad-mcp-server/.github/workflows/test.yml +40 -0
- package/mcp_servers/openscad-mcp-server/Dockerfile +29 -0
- package/mcp_servers/openscad-mcp-server/LICENSE +21 -0
- package/mcp_servers/openscad-mcp-server/README.md +154 -0
- package/mcp_servers/openscad-mcp-server/docs/audit.md +56 -0
- package/mcp_servers/openscad-mcp-server/docs/docker.md +66 -0
- package/mcp_servers/openscad-mcp-server/docs/issue-followup.md +19 -0
- package/mcp_servers/openscad-mcp-server/docs/jetson.md +17 -0
- package/mcp_servers/openscad-mcp-server/glama.json +4 -0
- package/mcp_servers/openscad-mcp-server/legacy/README.md +15 -0
- package/mcp_servers/openscad-mcp-server/legacy/README.original.md +294 -0
- package/mcp_servers/openscad-mcp-server/legacy/implementation_plan.md +100 -0
- package/mcp_servers/openscad-mcp-server/legacy/old/download_sam2_checkpoint.py +115 -0
- package/mcp_servers/openscad-mcp-server/legacy/old/src/ai/sam_segmentation.py +209 -0
- package/mcp_servers/openscad-mcp-server/legacy/old/src/models/threestudio_generator.py +231 -0
- package/mcp_servers/openscad-mcp-server/legacy/old/src/workflow/image_to_model_pipeline.py +260 -0
- package/mcp_servers/openscad-mcp-server/legacy/old/test_sam2_segmentation.py +96 -0
- package/mcp_servers/openscad-mcp-server/legacy/requirements.txt +57 -0
- package/mcp_servers/openscad-mcp-server/legacy/rtfmd/README.md +39 -0
- package/mcp_servers/openscad-mcp-server/legacy/rtfmd/decisions/ai-driven-code-generation.md +122 -0
- package/mcp_servers/openscad-mcp-server/legacy/rtfmd/decisions/export-formats.md +76 -0
- package/mcp_servers/openscad-mcp-server/legacy/rtfmd/files/src/ai/ai_service.py.md +51 -0
- package/mcp_servers/openscad-mcp-server/legacy/rtfmd/files/src/main.py.md +63 -0
- package/mcp_servers/openscad-mcp-server/legacy/rtfmd/files/src/models/code_generator.py.md +63 -0
- package/mcp_servers/openscad-mcp-server/legacy/rtfmd/files/src/nlp/parameter_extractor.py.md +63 -0
- package/mcp_servers/openscad-mcp-server/legacy/rtfmd/knowledge/ai/natural-language-processing.md +78 -0
- package/mcp_servers/openscad-mcp-server/legacy/rtfmd/knowledge/nlp/parameter-extraction.md +173 -0
- package/mcp_servers/openscad-mcp-server/legacy/rtfmd/knowledge/openscad/export-formats.md +91 -0
- package/mcp_servers/openscad-mcp-server/legacy/rtfmd/knowledge/openscad/openscad-basics.md +66 -0
- package/mcp_servers/openscad-mcp-server/legacy/rtfmd/knowledge/openscad/primitive-testing.md +79 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/__init__.py +0 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/ai/ai_service.py +257 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/ai/gemini_api.py +161 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/ai/venice_api.py +203 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/config.py +121 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/main.py +1456 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/main.py.new +404 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/main_remote.py +401 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/models/__init__.py +0 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/models/code_generator.py +321 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/models/cuda_mvs.py +209 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/models/scad_templates/basic_shapes.scad +144 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/nlp/__init__.py +0 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/nlp/parameter_extractor.py +388 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/openscad_wrapper/__init__.py +0 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/openscad_wrapper/wrapper.py +418 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/printer_discovery/__init__.py +1 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/printer_discovery/printer_discovery.py +471 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/remote/connection_manager.py +537 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/remote/cuda_mvs_client.py +435 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/remote/cuda_mvs_server.py +787 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/remote/error_handling.py +415 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/testing/__init__.py +0 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/testing/primitive_tester.py +203 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/testing/test_primitives.py +98 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/utils/__init__.py +1 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/utils/cad_exporter.py +241 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/utils/format_validator.py +206 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/utils/stl_exporter.py +140 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/utils/stl_repair.py +91 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/utils/stl_validator.py +123 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/visualization/__init__.py +0 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/visualization/headless_renderer.py +52 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/visualization/renderer.py +177 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/visualization/web_interface.py +639 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/workflow/image_approval.py +148 -0
- package/mcp_servers/openscad-mcp-server/legacy/src/workflow/multi_view_to_model_pipeline.py +338 -0
- package/mcp_servers/openscad-mcp-server/legacy/test_complete_workflow.py +374 -0
- package/mcp_servers/openscad-mcp-server/legacy/test_cuda_mvs.py +191 -0
- package/mcp_servers/openscad-mcp-server/legacy/test_gemini_api.py +168 -0
- package/mcp_servers/openscad-mcp-server/legacy/test_image_approval.py +192 -0
- package/mcp_servers/openscad-mcp-server/legacy/test_image_approval_workflow.py +251 -0
- package/mcp_servers/openscad-mcp-server/legacy/test_image_to_model_pipeline.py +145 -0
- package/mcp_servers/openscad-mcp-server/legacy/test_model_selection.py +41 -0
- package/mcp_servers/openscad-mcp-server/legacy/test_multi_view_pipeline.py +290 -0
- package/mcp_servers/openscad-mcp-server/legacy/test_primitives.sh +13 -0
- package/mcp_servers/openscad-mcp-server/legacy/test_rabbit_direct.py +71 -0
- package/mcp_servers/openscad-mcp-server/legacy/test_remote_cuda_mvs.py +283 -0
- package/mcp_servers/openscad-mcp-server/legacy/test_venice_example.py +69 -0
- package/mcp_servers/openscad-mcp-server/pyproject.toml +33 -0
- package/mcp_servers/openscad-mcp-server/requirements.txt +2 -0
- package/mcp_servers/openscad-mcp-server/scad/simple_cube.scad +2 -0
- package/mcp_servers/openscad-mcp-server/scripts/test_docker.py +220 -0
- package/mcp_servers/openscad-mcp-server/src/openscad_mcp/__init__.py +3 -0
- package/mcp_servers/openscad-mcp-server/src/openscad_mcp/__main__.py +3 -0
- package/mcp_servers/openscad-mcp-server/src/openscad_mcp/engine.py +94 -0
- package/mcp_servers/openscad-mcp-server/src/openscad_mcp/geometry.py +242 -0
- package/mcp_servers/openscad-mcp-server/src/openscad_mcp/server.py +245 -0
- package/mcp_servers/openscad-mcp-server/src/openscad_mcp/service.py +190 -0
- package/mcp_servers/openscad-mcp-server/tests/conftest.py +17 -0
- package/mcp_servers/openscad-mcp-server/tests/test_engine.py +37 -0
- package/mcp_servers/openscad-mcp-server/tests/test_geometry.py +106 -0
- package/mcp_servers/openscad-mcp-server/tests/test_integration.py +172 -0
- package/mcp_servers/openscad-mcp-server/tests/test_transports.py +314 -0
- package/mcp_servers/openscad-mcp-server/uv.lock +1123 -0
- package/package.json +44 -0
- package/scripts/install_mcps.sh +86 -0
- package/scripts/openscad_wrapper.sh +62 -0
- package/scripts/run_kicad_mcp.sh +25 -0
- package/scripts/run_openscad_mcp.sh +41 -0
- package/scripts/test_mcp_connection.py +626 -0
- package/scripts/test_mcp_connection.sh +168 -0
- package/skills/code-first-hardware-design/SKILL.md +260 -0
- package/skills/pcb-constraint-definition/SKILL.md +236 -0
- package/skills/pcb-layout-routing-automation/SKILL.md +153 -0
- package/skills/pcb-validation-dfm-signoff/SKILL.md +194 -0
- package/skills/schematic-datasheet-analysis/SKILL.md +202 -0
|
@@ -0,0 +1,1187 @@
|
|
|
1
|
+
"""Device tree generation tools for KiCad MCP Server.
|
|
2
|
+
|
|
3
|
+
This module provides device tree source (.dts) generation capabilities including:
|
|
4
|
+
- GPIO pin configuration extraction
|
|
5
|
+
- I2C/SPI/UART device detection
|
|
6
|
+
- Power domain analysis
|
|
7
|
+
- SOC-specific device tree generation
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
from jinja2 import Template
|
|
13
|
+
|
|
14
|
+
from ..parsers.netlist_parser import NetlistParser
|
|
15
|
+
from ..parsers.schematic_parser import SchematicParser
|
|
16
|
+
from ..server import mcp
|
|
17
|
+
|
|
18
|
+
# Device tree compatible strings database
|
|
19
|
+
DEVICE_TREE_BINDINGS = {
|
|
20
|
+
"Sensor": {
|
|
21
|
+
"BMP280": {"compatible": "bosch,bmp280"},
|
|
22
|
+
"BME280": {"compatible": "bosch,bme280"},
|
|
23
|
+
"BMP388": {"compatible": "bosch,bmp388"},
|
|
24
|
+
"LSM6DS3": {"compatible": "st,lsm6ds3"},
|
|
25
|
+
"MPU6050": {"compatible": "invensense,mpu6050"},
|
|
26
|
+
"MPU9250": {"compatible": "invensense,mpu9250"},
|
|
27
|
+
"HTS221": {"compatible": "st,hts221"},
|
|
28
|
+
"LPS22HB": {"compatible": "st,lps22hb"},
|
|
29
|
+
"DHT22": {"compatible": "dht22"},
|
|
30
|
+
"SHT30": {"compatible": "sensirion,sht3x"},
|
|
31
|
+
"MAX31855": {"compatible": "maxim,max31855"},
|
|
32
|
+
},
|
|
33
|
+
"Display": {
|
|
34
|
+
"ST7789": {"compatible": "sitronix,st7789"},
|
|
35
|
+
"ST7735": {"compatible": "sitronix,st7735"},
|
|
36
|
+
"ILI9341": {"compatible": "ilitek,ili9341"},
|
|
37
|
+
"SSD1306": {"compatible": "solomon,ssd1306"},
|
|
38
|
+
"SH1106": {"compatible": "sinowealth,sh1106"},
|
|
39
|
+
},
|
|
40
|
+
"Memory": {
|
|
41
|
+
"AT24C256": {"compatible": "atmel,24c256"},
|
|
42
|
+
"W25Q128": {"compatible": "jedec,spi-nor"},
|
|
43
|
+
"GD25Q16": {"compatible": "jedec,spi-nor"},
|
|
44
|
+
},
|
|
45
|
+
"Wireless": {
|
|
46
|
+
"ESP8266": {"compatible": "espressif,esp8266"},
|
|
47
|
+
"nRF24L01": {"compatible": "nordic,nrf24l01"},
|
|
48
|
+
"SX1278": {"compatible": "semtech,sx1278"},
|
|
49
|
+
},
|
|
50
|
+
"Connectivity": {
|
|
51
|
+
"CP2102": {"compatible": "silabs,cp2102"},
|
|
52
|
+
"FT232RL": {"compatible": "ftdi,ft232rl"},
|
|
53
|
+
"CH340G": {"compatible": "wch,ch340"},
|
|
54
|
+
},
|
|
55
|
+
"Power": {
|
|
56
|
+
"TP4056": {"compatible": "tp4056"},
|
|
57
|
+
"AXP192": {"compatible": "x-powers,axp192"},
|
|
58
|
+
"LP3985": {"compatible": "ti,lp3985"},
|
|
59
|
+
"AP2112": {"compatible": "tps,ap2112"},
|
|
60
|
+
},
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
# SOC-specific device tree templates
|
|
65
|
+
DEVICE_TREE_TEMPLATES = {
|
|
66
|
+
"stm32f4": """/* Device Tree for STM32F4 SOC */
|
|
67
|
+
/* Auto-generated from KiCad schematic */
|
|
68
|
+
|
|
69
|
+
#include <dt-bindings/gpio/gpio.h>
|
|
70
|
+
#include <dt-bindings/pinctrl/stm32-pinctrl.h>
|
|
71
|
+
|
|
72
|
+
/ {
|
|
73
|
+
model = "{{ board_name }}";
|
|
74
|
+
compatible = "st,stm32f4";
|
|
75
|
+
|
|
76
|
+
/* GPIO Configuration */
|
|
77
|
+
{% for pin in gpio_pins %}
|
|
78
|
+
gpio_{{ pin.name }}: gpio@{{ pin.number }} {
|
|
79
|
+
gpio-controller;
|
|
80
|
+
#gpio-cells = <2>;
|
|
81
|
+
status = "okay";
|
|
82
|
+
};
|
|
83
|
+
{% endfor %}
|
|
84
|
+
|
|
85
|
+
/* I2C Devices */
|
|
86
|
+
{% for bus in i2c_buses %}
|
|
87
|
+
i2c{{ bus.number }}: i2c@{{ bus.address }} {
|
|
88
|
+
compatible = "st,stm32-i2c";
|
|
89
|
+
#address-cells = <1>;
|
|
90
|
+
#size-cells = <0>;
|
|
91
|
+
status = "okay";
|
|
92
|
+
|
|
93
|
+
{% for device in bus.devices %}
|
|
94
|
+
{{ device.name }}: {{ device.compatible }}@{{ device.address }} {
|
|
95
|
+
compatible = "{{ device.compatible }}";
|
|
96
|
+
reg = <{{ device.address }}>;
|
|
97
|
+
{% if device.properties %}
|
|
98
|
+
{% for key, value in device.properties.items() %}
|
|
99
|
+
{{ key }} = <{{ value }}>;
|
|
100
|
+
{% endfor %}
|
|
101
|
+
{% endif %}
|
|
102
|
+
};
|
|
103
|
+
{% endfor %}
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
{% endfor %}
|
|
107
|
+
|
|
108
|
+
/* SPI Devices */
|
|
109
|
+
{% for bus in spi_buses %}
|
|
110
|
+
spi{{ bus.number }}: spi@{{ bus.address }} {
|
|
111
|
+
compatible = "st,stm32-spi";
|
|
112
|
+
#address-cells = <1>;
|
|
113
|
+
#size-cells = <0>;
|
|
114
|
+
status = "okay";
|
|
115
|
+
|
|
116
|
+
{% for device in bus.devices %}
|
|
117
|
+
{{ device.name }}: {{ device.compatible }}@{{ device.cs }} {
|
|
118
|
+
compatible = "{{ device.compatible }}";
|
|
119
|
+
reg = <{{ device.cs }}>;
|
|
120
|
+
spi-max-frequency = <{{ device.frequency }}>;
|
|
121
|
+
{% if device.properties %}
|
|
122
|
+
{% for key, value in device.properties.items() %}
|
|
123
|
+
{{ key }} = <{{ value }}>;
|
|
124
|
+
{% endfor %}
|
|
125
|
+
{% endif %}
|
|
126
|
+
};
|
|
127
|
+
{% endfor %}
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
{% endfor %}
|
|
131
|
+
|
|
132
|
+
/* UART Devices */
|
|
133
|
+
{% for uart in uarts %}
|
|
134
|
+
usart{{ uart.number }}: serial@{{ uart.address }} {
|
|
135
|
+
compatible = "st,stm32-usart";
|
|
136
|
+
status = "okay";
|
|
137
|
+
};
|
|
138
|
+
{% endfor %}
|
|
139
|
+
};
|
|
140
|
+
""",
|
|
141
|
+
"esp32": """/* Device Tree for ESP32 SOC */
|
|
142
|
+
/* Auto-generated from KiCad schematic */
|
|
143
|
+
|
|
144
|
+
#include <dt-bindings/gpio/gpio.h>
|
|
145
|
+
|
|
146
|
+
/ {
|
|
147
|
+
model = "{{ board_name }}";
|
|
148
|
+
compatible = "espressif,esp32";
|
|
149
|
+
|
|
150
|
+
/* GPIO Configuration */
|
|
151
|
+
{% for pin in gpio_pins %}
|
|
152
|
+
gpio_{{ pin.number }}: gpio@{{ pin.number }} {
|
|
153
|
+
gpio-controller;
|
|
154
|
+
#gpio-cells = <2>;
|
|
155
|
+
status = "okay";
|
|
156
|
+
};
|
|
157
|
+
{% endfor %}
|
|
158
|
+
|
|
159
|
+
/* I2C Devices */
|
|
160
|
+
{% for bus in i2c_buses %}
|
|
161
|
+
i2c{{ bus.number }}: i2c@{{ bus.address }} {
|
|
162
|
+
compatible = "espressif,esp32-i2c";
|
|
163
|
+
#address-cells = <1>;
|
|
164
|
+
#size-cells = <0>;
|
|
165
|
+
status = "okay";
|
|
166
|
+
|
|
167
|
+
{% for device in bus.devices %}
|
|
168
|
+
{{ device.name }}: {{ device.compatible }}@{{ device.address }} {
|
|
169
|
+
compatible = "{{ device.compatible }}";
|
|
170
|
+
reg = <{{ device.address }}>;
|
|
171
|
+
{% if device.properties %}
|
|
172
|
+
{% for key, value in device.properties.items() %}
|
|
173
|
+
{{ key }} = <{{ value }}>;
|
|
174
|
+
{% endfor %}
|
|
175
|
+
{% endif %}
|
|
176
|
+
};
|
|
177
|
+
{% endfor %}
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
{% endfor %}
|
|
181
|
+
|
|
182
|
+
/* SPI Devices */
|
|
183
|
+
{% for bus in spi_buses %}
|
|
184
|
+
spi{{ bus.number }}: spi@{{ bus.address }} {
|
|
185
|
+
compatible = "espressif,esp32-spi";
|
|
186
|
+
#address-cells = <1>;
|
|
187
|
+
#size-cells = <0>;
|
|
188
|
+
status = "okay";
|
|
189
|
+
|
|
190
|
+
{% for device in bus.devices %}
|
|
191
|
+
{{ device.name }}: {{ device.compatible }}@{{ device.cs }} {
|
|
192
|
+
compatible = "{{ device.compatible }}";
|
|
193
|
+
reg = <{{ device.cs }}>;
|
|
194
|
+
spi-max-frequency = <{{ device.frequency }}>;
|
|
195
|
+
{% if device.properties %}
|
|
196
|
+
{% for key, value in device.properties.items() %}
|
|
197
|
+
{{ key }} = <{{ value }}>;
|
|
198
|
+
{% endfor %}
|
|
199
|
+
{% endif %}
|
|
200
|
+
};
|
|
201
|
+
{% endfor %}
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
{% endfor %}
|
|
205
|
+
};
|
|
206
|
+
""",
|
|
207
|
+
"nrf52": """/* Device Tree for nRF52 SOC */
|
|
208
|
+
/* Auto-generated from KiCad schematic */
|
|
209
|
+
|
|
210
|
+
#include <dt-bindings/gpio/gpio.h>
|
|
211
|
+
|
|
212
|
+
/ {
|
|
213
|
+
model = "{{ board_name }}";
|
|
214
|
+
compatible = "nordic,nrf52";
|
|
215
|
+
|
|
216
|
+
/* GPIO Configuration */
|
|
217
|
+
{% for pin in gpio_pins %}
|
|
218
|
+
gpio_{{ pin.number }}: gpio@{{ pin.number }} {
|
|
219
|
+
gpio-controller;
|
|
220
|
+
#gpio-cells = <2>;
|
|
221
|
+
status = "okay";
|
|
222
|
+
};
|
|
223
|
+
{% endfor %}
|
|
224
|
+
|
|
225
|
+
/* I2C Devices */
|
|
226
|
+
{% for bus in i2c_buses %}
|
|
227
|
+
i2c{{ bus.number }}: i2c@{{ bus.address }} {
|
|
228
|
+
compatible = "nordic,nrf52-i2c";
|
|
229
|
+
#address-cells = <1>;
|
|
230
|
+
#size-cells = <0>;
|
|
231
|
+
status = "okay";
|
|
232
|
+
|
|
233
|
+
{% for device in bus.devices %}
|
|
234
|
+
{{ device.name }}: {{ device.compatible }}@{{ device.address }} {
|
|
235
|
+
compatible = "{{ device.compatible }}";
|
|
236
|
+
reg = <{{ device.address }}>;
|
|
237
|
+
{% if device.properties %}
|
|
238
|
+
{% for key, value in device.properties.items() %}
|
|
239
|
+
{{ key }} = <{{ value }}>;
|
|
240
|
+
{% endfor %}
|
|
241
|
+
{% endif %}
|
|
242
|
+
};
|
|
243
|
+
{% endfor %}
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
{% endfor %}
|
|
247
|
+
|
|
248
|
+
/* SPI Devices */
|
|
249
|
+
{% for bus in spi_buses %}
|
|
250
|
+
spi{{ bus.number }}: spi@{{ bus.address }} {
|
|
251
|
+
compatible = "nordic,nrf52-spi";
|
|
252
|
+
#address-cells = <1>;
|
|
253
|
+
#size-cells = <0>;
|
|
254
|
+
status = "okay";
|
|
255
|
+
|
|
256
|
+
{% for device in bus.devices %}
|
|
257
|
+
{{ device.name }}: {{ device.compatible }}@{{ device.cs }} {
|
|
258
|
+
compatible = "{{ device.compatible }}";
|
|
259
|
+
reg = <{{ device.cs }}>;
|
|
260
|
+
spi-max-frequency = <{{ device.frequency }}>;
|
|
261
|
+
{% if device.properties %}
|
|
262
|
+
{% for key, value in device.properties.items() %}
|
|
263
|
+
{{ key }} = <{{ value }}>;
|
|
264
|
+
{% endfor %}
|
|
265
|
+
{% endif %}
|
|
266
|
+
};
|
|
267
|
+
{% endfor %}
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
{% endfor %}
|
|
271
|
+
};
|
|
272
|
+
""",
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def _find_component_binding(component_value: str, component_type: str) -> dict | None:
|
|
277
|
+
"""Find device tree binding for a component.
|
|
278
|
+
|
|
279
|
+
Args:
|
|
280
|
+
component_value: Component value/part number
|
|
281
|
+
component_type: Component category (Sensor, Display, etc.)
|
|
282
|
+
|
|
283
|
+
Returns:
|
|
284
|
+
Device tree binding dict or None if not found
|
|
285
|
+
"""
|
|
286
|
+
if not component_value or not component_type:
|
|
287
|
+
return None
|
|
288
|
+
|
|
289
|
+
component_value_upper = component_value.upper()
|
|
290
|
+
|
|
291
|
+
# Try exact match first
|
|
292
|
+
if component_type in DEVICE_TREE_BINDINGS:
|
|
293
|
+
if component_value_upper in DEVICE_TREE_BINDINGS[component_type]:
|
|
294
|
+
return DEVICE_TREE_BINDINGS[component_type][component_value_upper]
|
|
295
|
+
|
|
296
|
+
# Try partial match
|
|
297
|
+
for key, value in DEVICE_TREE_BINDINGS[component_type].items():
|
|
298
|
+
if key in component_value_upper or component_value_upper in key:
|
|
299
|
+
return value
|
|
300
|
+
|
|
301
|
+
return None
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
def _infer_peripheral_type(net_name: str) -> str | None:
|
|
305
|
+
"""Infer peripheral type from net name.
|
|
306
|
+
|
|
307
|
+
Args:
|
|
308
|
+
net_name: Net name to analyze
|
|
309
|
+
|
|
310
|
+
Returns:
|
|
311
|
+
Peripheral type (I2C, SPI, UART, GPIO) or None
|
|
312
|
+
"""
|
|
313
|
+
if not net_name:
|
|
314
|
+
return None
|
|
315
|
+
|
|
316
|
+
net_name_upper = net_name.upper()
|
|
317
|
+
|
|
318
|
+
if "I2C" in net_name_upper or "TWI" in net_name_upper:
|
|
319
|
+
return "I2C"
|
|
320
|
+
elif "SPI" in net_name_upper:
|
|
321
|
+
return "SPI"
|
|
322
|
+
elif "UART" in net_name_upper or "USART" in net_name_upper or "SERIAL" in net_name_upper:
|
|
323
|
+
return "UART"
|
|
324
|
+
elif "GPIO" in net_name_upper or "IO" in net_name_upper:
|
|
325
|
+
return "GPIO"
|
|
326
|
+
|
|
327
|
+
return None
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
def _extract_i2c_address_from_net(net_name: str) -> int | None:
|
|
331
|
+
"""Extract I2C address from net name.
|
|
332
|
+
|
|
333
|
+
Args:
|
|
334
|
+
net_name: Net name (e.g., I2C_SDA_0x76)
|
|
335
|
+
|
|
336
|
+
Returns:
|
|
337
|
+
I2C address as integer or None
|
|
338
|
+
"""
|
|
339
|
+
import re
|
|
340
|
+
|
|
341
|
+
# Look for hex address pattern
|
|
342
|
+
match = re.search(r"0x[0-9A-Fa-f]+", net_name)
|
|
343
|
+
if match:
|
|
344
|
+
return int(match.group(0), 16)
|
|
345
|
+
|
|
346
|
+
# Look for decimal address pattern
|
|
347
|
+
match = re.search(r"[_\s](\d{2,3})$", net_name)
|
|
348
|
+
if match:
|
|
349
|
+
address = int(match.group(1))
|
|
350
|
+
if 1 <= address <= 127: # Valid I2C address range
|
|
351
|
+
return address
|
|
352
|
+
|
|
353
|
+
return None
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
@mcp.tool()
|
|
357
|
+
async def generate_device_tree(
|
|
358
|
+
schematic_path: str,
|
|
359
|
+
target_soc: str = "stm32f4",
|
|
360
|
+
output_path: str = "",
|
|
361
|
+
) -> str:
|
|
362
|
+
"""Generate device tree source (.dts) file from schematic.
|
|
363
|
+
|
|
364
|
+
This function analyzes a KiCad schematic and generates a device tree
|
|
365
|
+
source file compatible with the specified SOC family.
|
|
366
|
+
|
|
367
|
+
Args:
|
|
368
|
+
schematic_path: Path to .kicad_sch file
|
|
369
|
+
target_soc: Target SOC family (stm32f4, esp32, nrf52)
|
|
370
|
+
output_path: Optional output file path
|
|
371
|
+
|
|
372
|
+
Returns:
|
|
373
|
+
Generated device tree source code or error message
|
|
374
|
+
"""
|
|
375
|
+
try:
|
|
376
|
+
sch_path = Path(schematic_path)
|
|
377
|
+
if not sch_path.exists():
|
|
378
|
+
return f"X **Schematic file not found:** {schematic_path}"
|
|
379
|
+
|
|
380
|
+
# Validate SOC family
|
|
381
|
+
if target_soc not in DEVICE_TREE_TEMPLATES:
|
|
382
|
+
return f"""X **Unsupported SOC Family**
|
|
383
|
+
|
|
384
|
+
**Requested:** {target_soc}
|
|
385
|
+
**Supported:** {', '.join(DEVICE_TREE_TEMPLATES.keys())}
|
|
386
|
+
|
|
387
|
+
Please specify a supported SOC family."""
|
|
388
|
+
|
|
389
|
+
# Parse schematic
|
|
390
|
+
schematic_parser = SchematicParser(str(sch_path))
|
|
391
|
+
|
|
392
|
+
# Obtain a netlist (next to schematic, in temp, or freshly generated).
|
|
393
|
+
# generate_netlist writes to the temp dir — NOT next to the schematic —
|
|
394
|
+
# so we must look there too (previously this always raised FileNotFoundError).
|
|
395
|
+
from .pin_analysis import _ensure_netlist
|
|
396
|
+
|
|
397
|
+
netlist_path = await _ensure_netlist(sch_path)
|
|
398
|
+
if netlist_path is None:
|
|
399
|
+
return "X **Failed to generate netlist for device tree analysis**"
|
|
400
|
+
|
|
401
|
+
# Parse netlist
|
|
402
|
+
netlist_parser = NetlistParser(str(netlist_path))
|
|
403
|
+
|
|
404
|
+
# Extract device tree data
|
|
405
|
+
dt_data = {
|
|
406
|
+
"board_name": sch_path.stem,
|
|
407
|
+
"gpio_pins": [],
|
|
408
|
+
"i2c_buses": [],
|
|
409
|
+
"spi_buses": [],
|
|
410
|
+
"uarts": [],
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
# Analyze components and extract peripheral configuration
|
|
414
|
+
components = list(schematic_parser.get_components(recursive=True))
|
|
415
|
+
|
|
416
|
+
for component in components:
|
|
417
|
+
comp_ref = component.reference
|
|
418
|
+
comp_value = component.value
|
|
419
|
+
# Find device tree binding
|
|
420
|
+
for category, _bindings in DEVICE_TREE_BINDINGS.items():
|
|
421
|
+
binding = _find_component_binding(comp_value, category)
|
|
422
|
+
if binding:
|
|
423
|
+
# Get net connections. trace_connection(ref, pin) returns a
|
|
424
|
+
# dict {net, ...} (not a list); iterate the component's real
|
|
425
|
+
# pins from the netlist instead of guessing pin numbers 1..8.
|
|
426
|
+
connections = []
|
|
427
|
+
nl_comp = netlist_parser.get_components().get(comp_ref)
|
|
428
|
+
pin_numbers = list(nl_comp.pins.keys()) if nl_comp else []
|
|
429
|
+
for pin_num in pin_numbers:
|
|
430
|
+
try:
|
|
431
|
+
pin_conn = netlist_parser.trace_connection(comp_ref, str(pin_num))
|
|
432
|
+
net_name = pin_conn.get("net") if pin_conn else None
|
|
433
|
+
if net_name:
|
|
434
|
+
peripheral_type = _infer_peripheral_type(net_name)
|
|
435
|
+
connections.append({
|
|
436
|
+
"net": net_name,
|
|
437
|
+
"peripheral": peripheral_type,
|
|
438
|
+
"pin": pin_num,
|
|
439
|
+
})
|
|
440
|
+
except Exception:
|
|
441
|
+
continue
|
|
442
|
+
|
|
443
|
+
if connections:
|
|
444
|
+
# Determine peripheral type based on connections
|
|
445
|
+
i2c_nets = [c for c in connections if c["peripheral"] == "I2C"]
|
|
446
|
+
spi_nets = [c for c in connections if c["peripheral"] == "SPI"]
|
|
447
|
+
uart_nets = [c for c in connections if c["peripheral"] == "UART"]
|
|
448
|
+
|
|
449
|
+
if i2c_nets:
|
|
450
|
+
# I2C device
|
|
451
|
+
i2c_address = _extract_i2c_address_from_net(i2c_nets[0]["net"])
|
|
452
|
+
|
|
453
|
+
device = {
|
|
454
|
+
"name": comp_ref,
|
|
455
|
+
"compatible": binding["compatible"],
|
|
456
|
+
"address": i2c_address or 0x00,
|
|
457
|
+
"properties": {},
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
# Add to I2C bus. Bus MMIO base is SoC-specific; left
|
|
461
|
+
# empty instead of wrongly stamping an STM32 address on
|
|
462
|
+
# every target (esp32, nrf52, ...).
|
|
463
|
+
dt_data["i2c_buses"].append({
|
|
464
|
+
"number": 1, # Default to I2C1
|
|
465
|
+
"address": "",
|
|
466
|
+
"devices": [device],
|
|
467
|
+
})
|
|
468
|
+
|
|
469
|
+
elif spi_nets:
|
|
470
|
+
# SPI device
|
|
471
|
+
device = {
|
|
472
|
+
"name": comp_ref,
|
|
473
|
+
"compatible": binding["compatible"],
|
|
474
|
+
"cs": 0, # Default CS
|
|
475
|
+
"frequency": 1000000, # 1 MHz default
|
|
476
|
+
"properties": {},
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
# Add to SPI bus (address left empty — SoC-specific)
|
|
480
|
+
dt_data["spi_buses"].append({
|
|
481
|
+
"number": 1, # Default to SPI1
|
|
482
|
+
"address": "",
|
|
483
|
+
"devices": [device],
|
|
484
|
+
})
|
|
485
|
+
|
|
486
|
+
elif uart_nets:
|
|
487
|
+
# UART device (address left empty — SoC-specific)
|
|
488
|
+
dt_data["uarts"].append({
|
|
489
|
+
"number": 1, # Default to USART1
|
|
490
|
+
"address": "",
|
|
491
|
+
})
|
|
492
|
+
|
|
493
|
+
# Extract GPIO pins
|
|
494
|
+
for component in components:
|
|
495
|
+
comp_ref = component.reference
|
|
496
|
+
comp_value = component.value
|
|
497
|
+
|
|
498
|
+
# Check if this is an MCU
|
|
499
|
+
if any(soc in comp_value.upper() for soc in ["STM32", "ESP32", "NRF"]):
|
|
500
|
+
# Extract GPIO pins from netlist (iterate the MCU's real pins)
|
|
501
|
+
nl_comp = netlist_parser.get_components().get(comp_ref)
|
|
502
|
+
if nl_comp:
|
|
503
|
+
for pin_num in nl_comp.pins:
|
|
504
|
+
try:
|
|
505
|
+
pin_conn = netlist_parser.trace_connection(comp_ref, str(pin_num))
|
|
506
|
+
net_name = pin_conn.get("net") if pin_conn else None
|
|
507
|
+
if net_name and ("GPIO" in net_name.upper() or "IO" in net_name.upper()):
|
|
508
|
+
dt_data["gpio_pins"].append({
|
|
509
|
+
"number": pin_num,
|
|
510
|
+
"name": f"P{pin_num}",
|
|
511
|
+
})
|
|
512
|
+
except Exception:
|
|
513
|
+
continue
|
|
514
|
+
|
|
515
|
+
# Generate device tree from template
|
|
516
|
+
template = Template(DEVICE_TREE_TEMPLATES[target_soc])
|
|
517
|
+
device_tree = template.render(**dt_data)
|
|
518
|
+
|
|
519
|
+
# Save to file if output path specified
|
|
520
|
+
if output_path:
|
|
521
|
+
output = Path(output_path)
|
|
522
|
+
output.parent.mkdir(parents=True, exist_ok=True)
|
|
523
|
+
|
|
524
|
+
with open(output, "w") as f:
|
|
525
|
+
f.write(device_tree)
|
|
526
|
+
|
|
527
|
+
return f"""OK **Device Tree Generated Successfully**
|
|
528
|
+
|
|
529
|
+
**Schematic:** {schematic_path}
|
|
530
|
+
**Target SOC:** {target_soc}
|
|
531
|
+
**Output:** {output_path}
|
|
532
|
+
|
|
533
|
+
## Device Tree Contents
|
|
534
|
+
|
|
535
|
+
- **GPIO Pins:** {len(dt_data['gpio_pins'])}
|
|
536
|
+
- **I2C Buses:** {len(dt_data['i2c_buses'])}
|
|
537
|
+
- **SPI Buses:** {len(dt_data['spi_buses'])}
|
|
538
|
+
- **UARTs:** {len(dt_data['uarts'])}
|
|
539
|
+
|
|
540
|
+
The device tree source file has been generated and saved to the specified location.
|
|
541
|
+
|
|
542
|
+
## Next Steps
|
|
543
|
+
|
|
544
|
+
1. Review and customize the generated device tree
|
|
545
|
+
2. Compile with device tree compiler: `dtc -I dts -O dtb -o output.dtb {output_path}`
|
|
546
|
+
3. Test on target hardware
|
|
547
|
+
4. Iterate as needed"""
|
|
548
|
+
|
|
549
|
+
else:
|
|
550
|
+
# Return device tree content directly
|
|
551
|
+
return f"""OK **Device Tree Generated Successfully**
|
|
552
|
+
|
|
553
|
+
**Schematic:** {schematic_path}
|
|
554
|
+
**Target SOC:** {target_soc}
|
|
555
|
+
|
|
556
|
+
## Device Tree Contents
|
|
557
|
+
|
|
558
|
+
- **GPIO Pins:** {len(dt_data['gpio_pins'])}
|
|
559
|
+
- **I2C Buses:** {len(dt_data['i2c_buses'])}
|
|
560
|
+
- **SPI Buses:** {len(dt_data['spi_buses'])}
|
|
561
|
+
- **UARTs:** {len(dt_data['uarts'])}
|
|
562
|
+
|
|
563
|
+
## Generated Device Tree
|
|
564
|
+
|
|
565
|
+
```dts
|
|
566
|
+
{device_tree}
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
## Next Steps
|
|
570
|
+
|
|
571
|
+
1. Review and customize the generated device tree
|
|
572
|
+
2. Save to file: `{sch_path.stem}.dts`
|
|
573
|
+
3. Compile with device tree compiler: `dtc -I dts -O dtb -o output.dtb`
|
|
574
|
+
4. Test on target hardware"""
|
|
575
|
+
|
|
576
|
+
except Exception as e:
|
|
577
|
+
import traceback
|
|
578
|
+
|
|
579
|
+
return f"""X **Device Tree Generation Failed**
|
|
580
|
+
|
|
581
|
+
**Schematic:** {schematic_path}
|
|
582
|
+
**Target SOC:** {target_soc}
|
|
583
|
+
|
|
584
|
+
**Error:** {str(e)}
|
|
585
|
+
|
|
586
|
+
**Traceback:**
|
|
587
|
+
```
|
|
588
|
+
{traceback.format_exc()}
|
|
589
|
+
```"""
|
|
590
|
+
|
|
591
|
+
|
|
592
|
+
@mcp.tool()
|
|
593
|
+
async def extract_gpio_config(
|
|
594
|
+
schematic_path: str,
|
|
595
|
+
soc_family: str = "",
|
|
596
|
+
) -> str:
|
|
597
|
+
"""Extract GPIO pin configurations from schematic.
|
|
598
|
+
|
|
599
|
+
Args:
|
|
600
|
+
schematic_path: Path to .kicad_sch file
|
|
601
|
+
soc_family: Optional SOC family filter
|
|
602
|
+
|
|
603
|
+
Returns:
|
|
604
|
+
GPIO configuration report
|
|
605
|
+
"""
|
|
606
|
+
try:
|
|
607
|
+
sch_path = Path(schematic_path)
|
|
608
|
+
if not sch_path.exists():
|
|
609
|
+
return f"X **Schematic file not found:** {schematic_path}"
|
|
610
|
+
|
|
611
|
+
# Obtain netlist
|
|
612
|
+
from .pin_analysis import _ensure_netlist
|
|
613
|
+
|
|
614
|
+
netlist_path = await _ensure_netlist(sch_path)
|
|
615
|
+
if netlist_path is None:
|
|
616
|
+
return "X **Failed to generate netlist for GPIO extraction**"
|
|
617
|
+
|
|
618
|
+
netlist_parser = NetlistParser(str(netlist_path))
|
|
619
|
+
schematic_parser = SchematicParser(str(sch_path))
|
|
620
|
+
|
|
621
|
+
# pin number -> name from schematic lib_symbols
|
|
622
|
+
pin_name_of: dict[tuple[str, str], str] = {}
|
|
623
|
+
for c in schematic_parser.get_components(recursive=True):
|
|
624
|
+
for p in c.pins:
|
|
625
|
+
pin_name_of[(c.reference, str(p.get("number", "")))] = p.get("name", "")
|
|
626
|
+
|
|
627
|
+
# Extract GPIO configurations
|
|
628
|
+
gpio_configs = []
|
|
629
|
+
|
|
630
|
+
# NetlistParser.get_components() returns dict[ref, NetlistComponent];
|
|
631
|
+
# there is no get_all_components() (previous call always crashed).
|
|
632
|
+
for comp_ref, comp in netlist_parser.get_components().items():
|
|
633
|
+
comp_value = comp.value
|
|
634
|
+
|
|
635
|
+
# Filter by SOC family if specified
|
|
636
|
+
if soc_family and soc_family.lower() not in comp_value.lower():
|
|
637
|
+
continue
|
|
638
|
+
|
|
639
|
+
# Check if this is an MCU
|
|
640
|
+
if any(soc in comp_value.upper() for soc in ["STM32", "ESP32", "NRF", "ATMEGA"]):
|
|
641
|
+
for pin_num in comp.pins:
|
|
642
|
+
try:
|
|
643
|
+
pin_conn = netlist_parser.trace_connection(comp_ref, str(pin_num))
|
|
644
|
+
net_name = pin_conn.get("net") if pin_conn else None
|
|
645
|
+
if net_name and ("GPIO" in net_name.upper() or "IO" in net_name.upper()):
|
|
646
|
+
gpio_configs.append({
|
|
647
|
+
"component": comp_ref,
|
|
648
|
+
"pin_number": str(pin_num),
|
|
649
|
+
"pin_name": pin_name_of.get((comp_ref, str(pin_num)), str(pin_num)),
|
|
650
|
+
"net": net_name,
|
|
651
|
+
"soc": comp_value,
|
|
652
|
+
})
|
|
653
|
+
except Exception:
|
|
654
|
+
continue
|
|
655
|
+
|
|
656
|
+
if not gpio_configs:
|
|
657
|
+
return f"""WARN **No GPIO Configurations Found**
|
|
658
|
+
|
|
659
|
+
**Schematic:** {schematic_path}
|
|
660
|
+
{'**SOC Family Filter:** ' + soc_family if soc_family else ''}
|
|
661
|
+
|
|
662
|
+
No GPIO configurations were found in the schematic.
|
|
663
|
+
|
|
664
|
+
**Next Steps:**
|
|
665
|
+
1. Ensure schematic has MCU components
|
|
666
|
+
2. Check net names contain 'GPIO' or 'IO'
|
|
667
|
+
3. Try without SOC family filter"""
|
|
668
|
+
|
|
669
|
+
# Format results
|
|
670
|
+
result = f"""# GPIO Configuration Extraction
|
|
671
|
+
|
|
672
|
+
**Schematic:** {schematic_path}
|
|
673
|
+
{'**SOC Family:** ' + soc_family if soc_family else ''}
|
|
674
|
+
**Total GPIO Pins:** {len(gpio_configs)}
|
|
675
|
+
|
|
676
|
+
## GPIO Pin Details
|
|
677
|
+
|
|
678
|
+
| Component | Pin | Net | SOC |
|
|
679
|
+
|-----------|-----|-----|-----|
|
|
680
|
+
"""
|
|
681
|
+
|
|
682
|
+
for config in gpio_configs:
|
|
683
|
+
result += f"| {config['component']} | {config['pin_name']} ({config['pin_number']}) | {config['net']} | {config['soc']} |\n"
|
|
684
|
+
|
|
685
|
+
# Add code generation suggestions
|
|
686
|
+
result += "\n## Code Generation Suggestions\n\n"
|
|
687
|
+
|
|
688
|
+
# Group by SOC
|
|
689
|
+
soc_groups = {}
|
|
690
|
+
for config in gpio_configs:
|
|
691
|
+
soc = config["soc"]
|
|
692
|
+
if soc not in soc_groups:
|
|
693
|
+
soc_groups[soc] = []
|
|
694
|
+
soc_groups[soc].append(config)
|
|
695
|
+
|
|
696
|
+
for soc, configs in soc_groups.items():
|
|
697
|
+
result += f"### {soc}\n\n```c\n"
|
|
698
|
+
result += f"// GPIO configuration for {soc}\n\n"
|
|
699
|
+
|
|
700
|
+
for config in configs[:10]: # Show first 10
|
|
701
|
+
result += f"// {config['pin_name']} ({config['pin_number']}): {config['net']}\n"
|
|
702
|
+
if "STM32" in soc.upper():
|
|
703
|
+
result += f"GPIO_InitTypeDef gpio_{config['pin_name'].lower()};\n"
|
|
704
|
+
result += f"gpio_{config['pin_name'].lower()}.Pin = GPIO_PIN_{config['pin_number']};\n"
|
|
705
|
+
result += f"gpio_{config['pin_name'].lower()}.Mode = GPIO_MODE_OUTPUT_PP;\n"
|
|
706
|
+
result += f"HAL_GPIO_Init(GPIO{config['pin_name'][1]}, &gpio_{config['pin_name'].lower()});\n\n"
|
|
707
|
+
elif "ESP32" in soc.upper():
|
|
708
|
+
result += f"gpio_set_direction(GPIO{config['pin_number']}, GPIO_MODE_OUTPUT);\n\n"
|
|
709
|
+
|
|
710
|
+
if len(configs) > 10:
|
|
711
|
+
result += f"// ... and {len(configs) - 10} more GPIO pins\n"
|
|
712
|
+
|
|
713
|
+
result += "```\n\n"
|
|
714
|
+
|
|
715
|
+
return result
|
|
716
|
+
|
|
717
|
+
except Exception as e:
|
|
718
|
+
import traceback
|
|
719
|
+
|
|
720
|
+
return f"""X **GPIO Configuration Extraction Failed**
|
|
721
|
+
|
|
722
|
+
**Schematic:** {schematic_path}
|
|
723
|
+
{'**SOC Family:** ' + soc_family if soc_family else ''}
|
|
724
|
+
|
|
725
|
+
**Error:** {str(e)}
|
|
726
|
+
|
|
727
|
+
**Traceback:**
|
|
728
|
+
```
|
|
729
|
+
{traceback.format_exc()}
|
|
730
|
+
```"""
|
|
731
|
+
|
|
732
|
+
|
|
733
|
+
@mcp.tool()
|
|
734
|
+
async def extract_i2c_devices(
|
|
735
|
+
schematic_path: str,
|
|
736
|
+
) -> str:
|
|
737
|
+
"""Extract I2C bus and device configurations.
|
|
738
|
+
|
|
739
|
+
Args:
|
|
740
|
+
schematic_path: Path to .kicad_sch file
|
|
741
|
+
|
|
742
|
+
Returns:
|
|
743
|
+
I2C device configuration report
|
|
744
|
+
"""
|
|
745
|
+
try:
|
|
746
|
+
sch_path = Path(schematic_path)
|
|
747
|
+
if not sch_path.exists():
|
|
748
|
+
return f"X **Schematic file not found:** {schematic_path}"
|
|
749
|
+
|
|
750
|
+
# Obtain netlist
|
|
751
|
+
from .pin_analysis import _ensure_netlist
|
|
752
|
+
|
|
753
|
+
netlist_path = await _ensure_netlist(sch_path)
|
|
754
|
+
if netlist_path is None:
|
|
755
|
+
return "X **Failed to generate netlist for I2C extraction**"
|
|
756
|
+
|
|
757
|
+
netlist_parser = NetlistParser(str(netlist_path))
|
|
758
|
+
schematic_parser = SchematicParser(str(sch_path))
|
|
759
|
+
|
|
760
|
+
# Extract I2C devices
|
|
761
|
+
i2c_devices = []
|
|
762
|
+
|
|
763
|
+
components = list(schematic_parser.get_components(recursive=True))
|
|
764
|
+
|
|
765
|
+
for component in components:
|
|
766
|
+
comp_ref = component.reference
|
|
767
|
+
comp_value = component.value
|
|
768
|
+
# Check if this is an I2C device
|
|
769
|
+
for category, _bindings in DEVICE_TREE_BINDINGS.items():
|
|
770
|
+
binding = _find_component_binding(comp_value, category)
|
|
771
|
+
if binding:
|
|
772
|
+
nl_comp = netlist_parser.get_components().get(comp_ref)
|
|
773
|
+
pin_numbers = list(nl_comp.pins.keys()) if nl_comp else []
|
|
774
|
+
for pin_num in pin_numbers:
|
|
775
|
+
try:
|
|
776
|
+
pin_conn = netlist_parser.trace_connection(comp_ref, str(pin_num))
|
|
777
|
+
net_name = pin_conn.get("net") if pin_conn else None
|
|
778
|
+
if net_name and ("I2C" in net_name.upper() or "TWI" in net_name.upper()):
|
|
779
|
+
i2c_address = _extract_i2c_address_from_net(net_name)
|
|
780
|
+
i2c_devices.append({
|
|
781
|
+
"component": comp_ref,
|
|
782
|
+
"value": comp_value,
|
|
783
|
+
"compatible": binding["compatible"],
|
|
784
|
+
"address": i2c_address,
|
|
785
|
+
"net": net_name,
|
|
786
|
+
"pin": pin_num,
|
|
787
|
+
})
|
|
788
|
+
break
|
|
789
|
+
except Exception:
|
|
790
|
+
continue
|
|
791
|
+
|
|
792
|
+
if not i2c_devices:
|
|
793
|
+
return f"""WARN **No I2C Devices Found**
|
|
794
|
+
|
|
795
|
+
**Schematic:** {schematic_path}
|
|
796
|
+
|
|
797
|
+
No I2C devices were found in the schematic.
|
|
798
|
+
|
|
799
|
+
**Next Steps:**
|
|
800
|
+
1. Ensure schematic has I2C peripherals
|
|
801
|
+
2. Check net names contain 'I2C' or 'TWI'
|
|
802
|
+
3. Include I2C addresses in net names (e.g., I2C_SDA_0x76)"""
|
|
803
|
+
|
|
804
|
+
# Format results
|
|
805
|
+
result = f"""# I2C Device Extraction
|
|
806
|
+
|
|
807
|
+
**Schematic:** {schematic_path}
|
|
808
|
+
**Total I2C Devices:** {len(i2c_devices)}
|
|
809
|
+
|
|
810
|
+
## I2C Device Details
|
|
811
|
+
|
|
812
|
+
| Component | Device | Compatible | Address | Net |
|
|
813
|
+
|-----------|--------|------------|---------|-----|
|
|
814
|
+
"""
|
|
815
|
+
|
|
816
|
+
for device in i2c_devices:
|
|
817
|
+
address_str = f"0x{device['address']:02X}" if device['address'] else "Unknown"
|
|
818
|
+
result += f"| {device['component']} | {device['value']} | {device['compatible']} | {address_str} | {device['net']} |\n"
|
|
819
|
+
|
|
820
|
+
# Add device tree generation
|
|
821
|
+
result += "\n## Device Tree Configuration\n\n```dts\n"
|
|
822
|
+
result += "&i2c1 {\n"
|
|
823
|
+
result += " status = \"okay\";\n\n"
|
|
824
|
+
|
|
825
|
+
for device in i2c_devices:
|
|
826
|
+
address_str = f"0x{device['address']:02X}" if device['address'] else "0x00"
|
|
827
|
+
result += f" {device['component'].lower()}: {device['compatible']}@{address_str} {{\n"
|
|
828
|
+
result += f" compatible = \"{device['compatible']}\";\n"
|
|
829
|
+
result += f" reg = <{address_str}>;\n"
|
|
830
|
+
result += " };\n\n"
|
|
831
|
+
|
|
832
|
+
result += "};\n```"
|
|
833
|
+
|
|
834
|
+
return result
|
|
835
|
+
|
|
836
|
+
except Exception as e:
|
|
837
|
+
import traceback
|
|
838
|
+
|
|
839
|
+
return f"""X **I2C Device Extraction Failed**
|
|
840
|
+
|
|
841
|
+
**Schematic:** {schematic_path}
|
|
842
|
+
|
|
843
|
+
**Error:** {str(e)}
|
|
844
|
+
|
|
845
|
+
**Traceback:**
|
|
846
|
+
```
|
|
847
|
+
{traceback.format_exc()}
|
|
848
|
+
```"""
|
|
849
|
+
|
|
850
|
+
|
|
851
|
+
@mcp.tool()
|
|
852
|
+
async def extract_spi_devices(
|
|
853
|
+
schematic_path: str,
|
|
854
|
+
) -> str:
|
|
855
|
+
"""Extract SPI bus and device configurations.
|
|
856
|
+
|
|
857
|
+
Args:
|
|
858
|
+
schematic_path: Path to .kicad_sch file
|
|
859
|
+
|
|
860
|
+
Returns:
|
|
861
|
+
SPI device configuration report
|
|
862
|
+
"""
|
|
863
|
+
try:
|
|
864
|
+
sch_path = Path(schematic_path)
|
|
865
|
+
if not sch_path.exists():
|
|
866
|
+
return f"X **Schematic file not found:** {schematic_path}"
|
|
867
|
+
|
|
868
|
+
# Obtain netlist
|
|
869
|
+
from .pin_analysis import _ensure_netlist
|
|
870
|
+
|
|
871
|
+
netlist_path = await _ensure_netlist(sch_path)
|
|
872
|
+
if netlist_path is None:
|
|
873
|
+
return "X **Failed to generate netlist for SPI extraction**"
|
|
874
|
+
|
|
875
|
+
netlist_parser = NetlistParser(str(netlist_path))
|
|
876
|
+
schematic_parser = SchematicParser(str(sch_path))
|
|
877
|
+
|
|
878
|
+
# Extract SPI devices
|
|
879
|
+
spi_devices = []
|
|
880
|
+
|
|
881
|
+
components = list(schematic_parser.get_components(recursive=True))
|
|
882
|
+
|
|
883
|
+
for component in components:
|
|
884
|
+
comp_ref = component.reference
|
|
885
|
+
comp_value = component.value
|
|
886
|
+
|
|
887
|
+
# Check if this is an SPI device
|
|
888
|
+
for category, _bindings in DEVICE_TREE_BINDINGS.items():
|
|
889
|
+
binding = _find_component_binding(comp_value, category)
|
|
890
|
+
if binding:
|
|
891
|
+
nl_comp = netlist_parser.get_components().get(comp_ref)
|
|
892
|
+
pin_numbers = list(nl_comp.pins.keys()) if nl_comp else []
|
|
893
|
+
for pin_num in pin_numbers:
|
|
894
|
+
try:
|
|
895
|
+
pin_conn = netlist_parser.trace_connection(comp_ref, str(pin_num))
|
|
896
|
+
net_name = pin_conn.get("net") if pin_conn else None
|
|
897
|
+
if net_name and "SPI" in net_name.upper():
|
|
898
|
+
spi_devices.append({
|
|
899
|
+
"component": comp_ref,
|
|
900
|
+
"value": comp_value,
|
|
901
|
+
"compatible": binding["compatible"],
|
|
902
|
+
"net": net_name,
|
|
903
|
+
"pin": pin_num,
|
|
904
|
+
})
|
|
905
|
+
break
|
|
906
|
+
except Exception:
|
|
907
|
+
continue
|
|
908
|
+
|
|
909
|
+
if not spi_devices:
|
|
910
|
+
return f"""WARN **No SPI Devices Found**
|
|
911
|
+
|
|
912
|
+
**Schematic:** {schematic_path}
|
|
913
|
+
|
|
914
|
+
No SPI devices were found in the schematic.
|
|
915
|
+
|
|
916
|
+
**Next Steps:**
|
|
917
|
+
1. Ensure schematic has SPI peripherals
|
|
918
|
+
2. Check net names contain 'SPI'
|
|
919
|
+
3. Include CS signals in net naming"""
|
|
920
|
+
|
|
921
|
+
# Format results
|
|
922
|
+
result = f"""# SPI Device Extraction
|
|
923
|
+
|
|
924
|
+
**Schematic:** {schematic_path}
|
|
925
|
+
**Total SPI Devices:** {len(spi_devices)}
|
|
926
|
+
|
|
927
|
+
## SPI Device Details
|
|
928
|
+
|
|
929
|
+
| Component | Device | Compatible | Net |
|
|
930
|
+
|-----------|--------|------------|-----|
|
|
931
|
+
"""
|
|
932
|
+
|
|
933
|
+
for device in spi_devices:
|
|
934
|
+
result += f"| {device['component']} | {device['value']} | {device['compatible']} | {device['net']} |\n"
|
|
935
|
+
|
|
936
|
+
# Add device tree generation
|
|
937
|
+
result += "\n## Device Tree Configuration\n\n```dts\n"
|
|
938
|
+
result += "&spi1 {\n"
|
|
939
|
+
result += " status = \"okay\";\n\n"
|
|
940
|
+
|
|
941
|
+
for i, device in enumerate(spi_devices):
|
|
942
|
+
result += f" {device['component'].lower()}: {device['compatible']}@{i} {{\n"
|
|
943
|
+
result += f" compatible = \"{device['compatible']}\";\n"
|
|
944
|
+
result += f" reg = <{i}>;\n"
|
|
945
|
+
result += " spi-max-frequency = <1000000>;\n"
|
|
946
|
+
result += " };\n\n"
|
|
947
|
+
|
|
948
|
+
result += "};\n```"
|
|
949
|
+
|
|
950
|
+
return result
|
|
951
|
+
|
|
952
|
+
except Exception as e:
|
|
953
|
+
import traceback
|
|
954
|
+
|
|
955
|
+
return f"""X **SPI Device Extraction Failed**
|
|
956
|
+
|
|
957
|
+
**Schematic:** {schematic_path}
|
|
958
|
+
|
|
959
|
+
**Error:** {str(e)}
|
|
960
|
+
|
|
961
|
+
**Traceback:**
|
|
962
|
+
```
|
|
963
|
+
{traceback.format_exc()}
|
|
964
|
+
```"""
|
|
965
|
+
|
|
966
|
+
|
|
967
|
+
@mcp.tool()
|
|
968
|
+
async def extract_power_domains(
|
|
969
|
+
schematic_path: str,
|
|
970
|
+
) -> str:
|
|
971
|
+
"""Extract power domain and regulator configurations.
|
|
972
|
+
|
|
973
|
+
Args:
|
|
974
|
+
schematic_path: Path to .kicad_sch file
|
|
975
|
+
|
|
976
|
+
Returns:
|
|
977
|
+
Power domain configuration report
|
|
978
|
+
"""
|
|
979
|
+
try:
|
|
980
|
+
sch_path = Path(schematic_path)
|
|
981
|
+
if not sch_path.exists():
|
|
982
|
+
return f"X **Schematic file not found:** {schematic_path}"
|
|
983
|
+
|
|
984
|
+
# Parse schematic
|
|
985
|
+
schematic_parser = SchematicParser(str(sch_path))
|
|
986
|
+
|
|
987
|
+
# Extract power components
|
|
988
|
+
power_components = []
|
|
989
|
+
|
|
990
|
+
components = list(schematic_parser.get_components(recursive=True))
|
|
991
|
+
|
|
992
|
+
for component in components:
|
|
993
|
+
comp_ref = component.reference
|
|
994
|
+
comp_value = component.value
|
|
995
|
+
comp_library = component.library_id
|
|
996
|
+
|
|
997
|
+
# Check if this is a power-related component
|
|
998
|
+
power_keywords = ["REG", "LDO", "BUCK", "BOOST", "TPS", "AP", "LP", "AXP", "PMIC", "NPM"]
|
|
999
|
+
if any(keyword in comp_value.upper() for keyword in power_keywords):
|
|
1000
|
+
power_components.append({
|
|
1001
|
+
"reference": comp_ref,
|
|
1002
|
+
"value": comp_value,
|
|
1003
|
+
"library": comp_library,
|
|
1004
|
+
})
|
|
1005
|
+
|
|
1006
|
+
if not power_components:
|
|
1007
|
+
return f"""WARN **No Power Components Found**
|
|
1008
|
+
|
|
1009
|
+
**Schematic:** {schematic_path}
|
|
1010
|
+
|
|
1011
|
+
No power management components were found in the schematic.
|
|
1012
|
+
|
|
1013
|
+
**Next Steps:**
|
|
1014
|
+
1. Check for regulators, LDOs, power ICs
|
|
1015
|
+
2. Verify component values contain power-related keywords"""
|
|
1016
|
+
|
|
1017
|
+
# Format results
|
|
1018
|
+
result = f"""# Power Domain Extraction
|
|
1019
|
+
|
|
1020
|
+
**Schematic:** {schematic_path}
|
|
1021
|
+
**Total Power Components:** {len(power_components)}
|
|
1022
|
+
|
|
1023
|
+
## Power Components
|
|
1024
|
+
|
|
1025
|
+
| Reference | Component | Type |
|
|
1026
|
+
|-----------|-----------|------|
|
|
1027
|
+
"""
|
|
1028
|
+
|
|
1029
|
+
for comp in power_components:
|
|
1030
|
+
# Determine component type
|
|
1031
|
+
comp_type = "Regulator"
|
|
1032
|
+
if "LDO" in comp["value"].upper():
|
|
1033
|
+
comp_type = "LDO Regulator"
|
|
1034
|
+
elif "BUCK" in comp["value"].upper():
|
|
1035
|
+
comp_type = "Buck Converter"
|
|
1036
|
+
elif "BOOST" in comp["value"].upper():
|
|
1037
|
+
comp_type = "Boost Converter"
|
|
1038
|
+
elif "PMIC" in comp["value"].upper() or "NPM" in comp["value"].upper():
|
|
1039
|
+
comp_type = "PMIC"
|
|
1040
|
+
|
|
1041
|
+
result += f"| {comp['reference']} | {comp['value']} | {comp_type} |\n"
|
|
1042
|
+
|
|
1043
|
+
# Add device tree generation suggestions
|
|
1044
|
+
result += "\n## Device Tree Configuration\n\n```dts\n"
|
|
1045
|
+
result += "/ {\n"
|
|
1046
|
+
result += " regulators {\n\n"
|
|
1047
|
+
|
|
1048
|
+
for comp in power_components[:5]: # Limit to first 5
|
|
1049
|
+
result += f" {comp['reference'].lower()}: {comp['reference'].lower()} {{\n"
|
|
1050
|
+
result += " compatible = \"regulator-fixed\";\n"
|
|
1051
|
+
result += " regulator-name = \"" + comp['reference'].lower() + "\";\n"
|
|
1052
|
+
result += " regulator-min-microvolt = <3300000>;\n"
|
|
1053
|
+
result += " regulator-max-microvolt = <3300000>;\n"
|
|
1054
|
+
result += " };\n\n"
|
|
1055
|
+
|
|
1056
|
+
if len(power_components) > 5:
|
|
1057
|
+
result += " // ... and " + str(len(power_components) - 5) + " more regulators\n"
|
|
1058
|
+
|
|
1059
|
+
result += " };\n"
|
|
1060
|
+
result += "};\n```"
|
|
1061
|
+
|
|
1062
|
+
return result
|
|
1063
|
+
|
|
1064
|
+
except Exception as e:
|
|
1065
|
+
import traceback
|
|
1066
|
+
|
|
1067
|
+
return f"""X **Power Domain Extraction Failed**
|
|
1068
|
+
|
|
1069
|
+
**Schematic:** {schematic_path}
|
|
1070
|
+
|
|
1071
|
+
**Error:** {str(e)}
|
|
1072
|
+
|
|
1073
|
+
**Traceback:**
|
|
1074
|
+
```
|
|
1075
|
+
{traceback.format_exc()}
|
|
1076
|
+
```"""
|
|
1077
|
+
|
|
1078
|
+
|
|
1079
|
+
@mcp.tool()
|
|
1080
|
+
async def validate_pin_configuration(
|
|
1081
|
+
schematic_path: str,
|
|
1082
|
+
) -> str:
|
|
1083
|
+
"""Validate pin configuration conflicts for device tree generation.
|
|
1084
|
+
|
|
1085
|
+
This function validates that the pin configuration is suitable for
|
|
1086
|
+
device tree generation and reports any conflicts or issues.
|
|
1087
|
+
|
|
1088
|
+
Args:
|
|
1089
|
+
schematic_path: Path to .kicad_sch file
|
|
1090
|
+
|
|
1091
|
+
Returns:
|
|
1092
|
+
Validation report with conflicts and recommendations
|
|
1093
|
+
"""
|
|
1094
|
+
try:
|
|
1095
|
+
sch_path = Path(schematic_path)
|
|
1096
|
+
if not sch_path.exists():
|
|
1097
|
+
return f"X **Schematic file not found:** {schematic_path}"
|
|
1098
|
+
|
|
1099
|
+
# First, run pin conflict detection
|
|
1100
|
+
from .pin_analysis import detect_pin_conflicts
|
|
1101
|
+
|
|
1102
|
+
conflict_result = await detect_pin_conflicts(str(sch_path))
|
|
1103
|
+
|
|
1104
|
+
if "X" in conflict_result:
|
|
1105
|
+
return f"""X **Pin Configuration Validation Failed**
|
|
1106
|
+
|
|
1107
|
+
The schematic has pin conflicts that must be resolved before
|
|
1108
|
+
device tree generation.
|
|
1109
|
+
|
|
1110
|
+
{conflict_result}
|
|
1111
|
+
|
|
1112
|
+
## Device Tree Generation Blocked
|
|
1113
|
+
|
|
1114
|
+
Pin conflicts prevent reliable device tree generation. Please
|
|
1115
|
+
resolve all conflicts before attempting device tree generation."""
|
|
1116
|
+
|
|
1117
|
+
# Check for MCU components
|
|
1118
|
+
schematic_parser = SchematicParser(str(sch_path))
|
|
1119
|
+
components = list(schematic_parser.get_components(recursive=True))
|
|
1120
|
+
|
|
1121
|
+
mcu_found = False
|
|
1122
|
+
for component in components:
|
|
1123
|
+
comp_value = component.value
|
|
1124
|
+
if any(soc in comp_value.upper() for soc in ["STM32", "ESP32", "NRF", "ATMEGA"]):
|
|
1125
|
+
mcu_found = True
|
|
1126
|
+
break
|
|
1127
|
+
|
|
1128
|
+
if not mcu_found:
|
|
1129
|
+
return f"""WARN **No MCU Component Found**
|
|
1130
|
+
|
|
1131
|
+
**Schematic:** {schematic_path}
|
|
1132
|
+
|
|
1133
|
+
Device tree generation requires an MCU component in the schematic.
|
|
1134
|
+
|
|
1135
|
+
**Supported MCU families:**
|
|
1136
|
+
- STM32 (STM32F, STM32H, STM32L series)
|
|
1137
|
+
- ESP32 (ESP32, ESP32-S2, ESP32-S3, etc.)
|
|
1138
|
+
- nRF52 (nRF52832, nRF52840, etc.)
|
|
1139
|
+
- ATmega (ATmega328P, ATmega2560, etc.)
|
|
1140
|
+
|
|
1141
|
+
**Next Steps:**
|
|
1142
|
+
1. Add an MCU component to the schematic
|
|
1143
|
+
2. Verify component value matches supported patterns
|
|
1144
|
+
3. Re-run validation"""
|
|
1145
|
+
|
|
1146
|
+
return f"""OK **Pin Configuration Validation Passed**
|
|
1147
|
+
|
|
1148
|
+
**Schematic:** {schematic_path}
|
|
1149
|
+
|
|
1150
|
+
The schematic is ready for device tree generation.
|
|
1151
|
+
|
|
1152
|
+
## Validation Results
|
|
1153
|
+
|
|
1154
|
+
- OK No pin conflicts detected
|
|
1155
|
+
- OK MCU component found
|
|
1156
|
+
- OK Net connections valid
|
|
1157
|
+
- OK Pin assignments compatible
|
|
1158
|
+
|
|
1159
|
+
## Ready for Device Tree Generation
|
|
1160
|
+
|
|
1161
|
+
You can now proceed with device tree generation:
|
|
1162
|
+
|
|
1163
|
+
```bash
|
|
1164
|
+
# Generate device tree
|
|
1165
|
+
generate_device_tree(
|
|
1166
|
+
schematic_path="{schematic_path}",
|
|
1167
|
+
target_soc="stm32f4",
|
|
1168
|
+
output_path="{sch_path.stem}.dts"
|
|
1169
|
+
)
|
|
1170
|
+
```
|
|
1171
|
+
|
|
1172
|
+
**Supported SOC families:** {', '.join(DEVICE_TREE_TEMPLATES.keys())}"""
|
|
1173
|
+
|
|
1174
|
+
except Exception as e:
|
|
1175
|
+
import traceback
|
|
1176
|
+
|
|
1177
|
+
return f"""X **Validation Failed**
|
|
1178
|
+
|
|
1179
|
+
**Schematic:** {schematic_path}
|
|
1180
|
+
|
|
1181
|
+
**Error:** {str(e)}
|
|
1182
|
+
|
|
1183
|
+
**Traceback:**
|
|
1184
|
+
```
|
|
1185
|
+
{traceback.format_exc()}
|
|
1186
|
+
```
|
|
1187
|
+
"""
|