Content Creation
#design
ato
Authoritative ato authoring and review skill: language reference, stdlib, design patterns, and end-to-end board design workflow.
DeepseekModel
Curated skill
Quality Excellent · 90
v1.0.0
Get
https://deepseekmodel.com/api/download.php?id=atopile-atopile-claude-skills-ato-skill-md&format=skill
Download .skill
Standard format with system_prompt and model_config, ready for any agent framework
The actual content of the system_prompt field in the .skill file.
name ato description Authoritative ato authoring and review skill: language reference, stdlib, design patterns, and end-to-end board design workflow. 1. End-to-End Design Process This is the canonical sequence for designing a board in atopile. Move quickly, keep the structure clean, and avoid spreading planning state across multiple rounds unless the design genuinely requires it. Step 1: Draft The Architecture Capture user intent as ato code immediately. Start with a clean high-level architecture and only stop to ask batched design questions when there are real unresolved decisions. Focus on: What the system is supposed to do The main functional blocks The important interfaces and voltage domains Key constraints on size, cost, power, or manufacturing Any parts or protocols that are already fixed by the user Tools: Use design_questions to batch multiple unresolved decisions at once. Use web_search if you need to research unfamiliar domains or components before locking the architecture. Gate: a spec .ato file exists with module hierarchy, interface connections, requirements in docstrings, and formal constraints. Step 2: Write The Spec The spec IS the design file at a high level of abstraction. As you implement, you fill in real components and wiring. The file grows; the structure stays. Key principles: Good naming — name modules by their role in the system, not implementation topology (see Section 1.1). Module boundaries should encapsulate common functionality to avoid duplication at the top level. Use high-level interfaces ( ElectricPower , I2C , SPI , UART , ElectricLogic ) instead of low-level electrical connections where possible. Custom interfaces are rare — before defining a new interface , check the stdlib first with stdlib_list / stdlib_get_item . If an existing stdlib interface or a simple composition/array of stdlib interfaces works, use that instead. Capture requirements in the module docstring under a Requirements: section on the module that owns them. Add formal constraints with assert for voltage, current, frequency bounds. Wire modules together at the interface level ( ~ ). Do NOT wire pins yet. Step-by-step: Break the request into subsystems. Each functional block becomes a module — power, MCU, sensors, comms, IO, etc. Define interfaces at module boundaries. Use stdlib interfaces to declare how modules connect. Capture requirements in docstrings. Add a Requirements: section to the docstring of the module that owns each requirement. Add formal constraints with assert for voltage, current, frequency bounds. Wire modules together at the interface level ( ~ ). Create a checklist linking items to requirement IDs for tracking. Example spec: import ElectricPower import I2C import SPI import ElectricLogic module SensorBoard: """ # Environmental Sensor Board Battery-powered sensor node with temperature, humidity, and pressure sensing, BLE comms, and USB-C charging. ## Requirements - R1: BLE connectivity — nRF52840 with BLE 5.0 - R2: Environmental sensing — BME280 for temp/humidity/pressure - R3: USB-C charging — 5V USB-C input with charge IC - R4: Board size — 25mm x 30mm max ## Key Decisions - nRF52840 for BLE + low power - BME280 for temp/humidity/pressure """ # ── Architecture ────────────────────────────────────── power = new PowerSupply mcu = new MCU sensors = new EnvironmentalSensor comms = new Radio # Interface-level wiring (no pins yet) power.rail_3v3 ~ mcu.power power.rail_3v3 ~ sensors.power mcu.i2c ~ sensors.i2c mcu.spi ~ comms.spi # ── Constraints ─────────────────────────────────────── assert power.usb_in.voltage within 4.5V to 5.5V assert power.rail_3v3.voltage within 3.3V +/- 5% module PowerSupply: """ USB-C input, charge controller, LDO regulation. ## Requirements - R5: Battery charging — LiPo charge IC with thermal protection """ usb_in = new ElectricPower battery = new ElectricPower rail_3v3 = new ElectricPower module MCU: """nRF52840 with crystal, decoupling, and debug header.""" power = new ElectricPower i2c = new I2C spi = new SPI module EnvironmentalSensor: """BME280 environmental sensor.""" power = new ElectricPower i2c = new I2C module Radio: """BLE antenna matching and RF front end.""" spi = new SPI Key rules for this step: Module names are final — PowerSupply stays PowerSupply through implementation. Do NOT suffix with "Spec". Place requirements in the docstring of the module that owns them, not all on the top-level. Use docstrings for overview, requirements, and important decisions. Do not keep unresolved planning state in the design file longer than needed; use design_questions to batch open questions and then continue implementation. Tools: Use stdlib_list / stdlib_get_item to check available interfaces and components before defining custom ones. Use examples_search / examples_read_ato to find reference designs for similar systems. Gate: architecture is coherent enough to implement. If there are multiple open design decisions, batch them with design_questions and continue once answers arrive. Step 3: Resolve Open Decisions Present the user with the current architecture and any real unresolved decisions: List the modules and their responsibilities. Show the interface connections between modules. Highlight any key decisions or trade-offs made. Call out any assumptions or areas where alternatives exist. Use design_questions to batch unresolved decisions instead of trickling follow-up questions across multiple turns. Then incorporate the answers directly into the spec and continue implementation. Gate: the key open questions are resolved or reasonable defaults have been chosen. Step 4: Implement Detailed Design Now fill in the spec with real components, wiring, and constraints. This step covers package search, part selection, and detailed wiring. 4a: Find existing packages Search the atopile package registry before building from scratch. Tools: packages_search → packages_install → package_ato_read to inspect public interface. Also check stdlib_list for built-in modules. Prefer reusing a well-tested package over writing a new driver module. 4b: Create local packages when none exist When packages_search returns no match for a needed IC, connector, or module, create a local driver package instead of giving up or asking the user to find one. Tools: parts_search → web_search (to compare families, inspect the vendor datasheet/design guide, validate topology, and find reference circuits) → parts_install(create_package=true) → project_read_file (to inspect the generated wrapper package) → project_edit_file (to refine that wrapper in place) → workspace_list_targets (to discover nested package targets). Step-by-step recipe: Find the part : Use parts_search to find the LCSC component (e.g., parts_search("LAN8742A") ). Research the part family when needed : Use web_search before locking the part if you need application notes, common reference circuits, family comparisons, or confirmation that the chosen topology is standard and robust. Install as a local package : Use parts_install with the LCSC ID and create_package=true . This installs the raw part and generates the canonical reusable wrapper package under packages/ . Inspect the vendor docs with web search : Use web_search with the part number, vendor, and terms like datasheet , hardware design , application circuit , decoupling , pinout , or the specific pins/features you need. Read the generated files : Inspect the generated wrapper under packages/<PartName>/<PartName>.ato and the installed raw part it imports to see available interfaces and exact pin names. Refine the wrapper package if needed: Treat packages/<PartName>/<PartName>.ato as the canonical wrapper module for that part. Edit that generated package file in place rather than creating another wrapper layer. Keep the raw installed part file unchanged. Start with a basic reusable wrapper first. Expose the minimum standard interfaces needed to build the package and integrate it cleanly. Keep the wrapper generic and reusable. Expose the chip's general capabilities, not one project's exact architecture. Expose standard interfaces such as ElectricPower , I2C , SPI , UART , CAN , SWD , USB2_0 , USB2_0_IF , ElectricLogic , or ElectricSignal . Before writing any custom interface , check stdlib_list / stdlib_get_item for an existing stdlib interface and prefer stdlib arrays/composition over project-local aggregate interfaces. Prefer capability-oriented names and boundaries such as uart , spi , adc_inputs , gpio , usb , swd , power , not design-specific roles like sbus , phase_current , weapon_pwm , or battlebot_interfaces . It is fine to make slightly opinionated pin choices so key capabilities are wired out cleanly, but do not encode one specific end design into the wrapper shape. Do not treat incomplete pin exposure as a blocker. Add more interfaces, alternate pin mappings, or richer capabilities later when integration proves they are needed. Map the internal _package component pins to those interfaces. Add decoupling capacitors and required passives. Set voltage/current constraints from the datasheet. If the wrapper needs new supporting physical parts while you are validating the package target in isolation, install them into that package project with parts_install(project_path="packages/<PartName>") . Discover targets : Run workspace_list_targets after package creation to inspect and build the package targets that were exposed automatically. Import and use the local package in your top-level design directly from packages/<PartName>/<PartName>.ato . Delegate package work when helpful : If the package project exists and can be built independently, use package_agent_spawn(project_path="packages/<PartName>", goal=..., comments=...) so a package specialist can refine that wrapper while you continue top-level integration. Example: refining a generated local I2C mux wrapper The generated package file under packages/<PartName>/<PartName>.ato is the wrapper you should refine. The raw part component it imports is not the place to edit behavior. #pragma experiment("BRIDGE_CONNECT") import ElectricPower import ElectricLogic import I2C import Capacitor import Resistor from "parts/Texas_Instruments_TCA9548APWR/Texas_Instruments_TCA9548APWR.ato" import Texas_Instruments_TCA9548APWR_package module TI_TCA9548A: # Public interfaces power = new ElectricPower assert power.voltage within 1.65V to 5.5V i2c = new I2C reset = new ElectricLogic # Instantiate the auto-generated package component package = new Texas_Instruments_TCA9548APWR_package # Power connections power.hv ~ package.VCC power.lv ~ package.GND # I2C — connect via .line and .reference i2c.sda.line ~ package.SDA i2c.scl.line ~ package.SCL i2c.sda.reference ~ power i2c.scl.reference ~ power # Decoupling — use bridge connect (~>) for series path decoup_100n = new Capacitor decoup_100n.capacitance = 100nF +/- 20% decoup_100n.package = "0402" power.hv ~> decoup_100n ~> power.lv decoup_2u2 = new Capacitor decoup_2u2.capacitance = 2.2uF +/- 20% decoup_2u2.package = "0402" power.hv ~> decoup_2u2 ~> power.lv # Reset with pullup reset.line ~ package.nRESET reset.reference ~ power reset_pullup = new Resistor reset_pullup.resistance = 10kohm +/- 1% reset_pullup.package = "0402" reset.line ~> reset_pullup ~> reset.reference.hv Key rules: Always parts_install first — never reference a part that hasn't been installed. Prefer parts_install(create_package=true) for ICs and other reusable wrapped parts. When validating a package as its own project, use parts_install(project_path="packages/<name>") for any new supporting parts the package itself imports. Use package_create_local only when you need an empty local package scaffold without installing a physical part. Always read the generated package and raw part .ato files to see the exact signal names (e.g., package.VCC , package.SDA ). Do NOT guess pin names. Always use web_search to inspect the vendor datasheet and hardware design notes to get correct pin mapping, constraints, and recommended decoupling. The generated package file under packages/ is the canonical wrapper for that part. Refine it in place. The raw installed file is a component — never edit it. Build a basic reusable wrapper first. Expose the minimum standard interfaces needed to validate the package and integrate it, then come back and add more pin mappings or interfaces later if integration requires them. Once a package project exists, prefer delegating isolated wrapper build-out through package_agent_spawn instead of doing all package work serially in the main agent. Instantiate the raw component inside the wrapper as package = new <ComponentName> . main.ato should import wrapper packages directly from packages/<name>/<name>.ato , not through an extra aggregator wrapper file. Connect interfaces via .line and .reference (e.g., i2c.sda.line ~ package.SDA ; i2c.sda.reference ~ power ). Use bridge connect ~> for decoupling caps in series (e.g., power.hv ~> cap ~> power.lv ). Use .capacitance for Capacitor values, .resistance for Resistor values (NOT .value ). Add #pragma experiment("BRIDGE_CONNECT") if using ~> . Keep IC-specific pin wiring inside the driver module; expose only abstract interfaces. Do NOT skip this step and tell the user to create the package themselves. This is core agent capability. 4c: Part selection Choose components using generics + constraints wherever possible. Tools: parts_search / parts_install for specific ICs/connectors. web_search for vendor datasheets, hardware design guides, application notes, and alternative parts. Use stdlib generics ( Resistor , Capacitor , Inductor , Diode , LED , Fuse ) with value + package constraints for auto-picking. Prefer generics over locked parts. Use parts_search only when a specific part is needed (IC, connector, specialized component). Use web_search before locking a part when you need to compare candidate families, confirm the recommended implementation pattern, or find a solid reference circuit/application note. Use parts_install for parts that need explicit LCSC IDs, and prefer create_package=true when the part should become a reusable local wrapper. Use web_search after selecting a concrete part to inspect the vendor datasheet, exact pins, limits, and supporting circuitry. Lock only high-risk parts (MCU, PMIC, RF, connectors). Leave commodity passives auto-picked. Before inventing a project-local interface , check whether the wrapper boundary can be represented as: a stdlib interface ( SPI , UART , SWD , USB2_0_IF , etc.) an array of stdlib signals/interfaces ( new ElectricLogic[3] , new ElectricPower[3] , new ElectricSignal[3] ) a few named stdlib fields directly on the module Only define a custom interface when it represents a real reusable protocol/boundary that stdlib or simple composition does not already cover. Keep package wrappers generic. Design-specific grouping and role naming belong in main.ato or project modules above the package layer. 4d: Detailed wiring and constraints Wire connectivity, add constraints and equations, complete the design. Wire modules through interfaces using ~ (or ~> for bridge/series paths). Add parameter constraints ( assert ... within ... ) for all key electrical properties. Add decoupling, pullups, and protection per Section 4 patterns. Gate: design is complete — all modules wired, all constraints declared, all interfaces connected. Every component is either a constrained generic or an explicitly selected part. Step 5: Build Run builds and fix issues iteratively until everything passes. Build submodules first (if applicable) — it is much easier to get small chunks working before running the full build. 5a: Build + fix loop Tools: workspace_list_targets → build_run → build_logs_search (filter by log_levels / stage ) → design_diagnostics for silent failures. Use report_variables to inspect constraint state and report_bom to verify part selection. Run workspace_list_targets first after creating/installing local packages so you know which package targets already exist automatically. Split the design into sensible submodules and build those smaller targets first. This is the default validation loop. Build wrapper/package targets first, and do so in parallel where practical, so you get feedback much faster than waiting on repeated full-design builds. If a wrapper is only partially exposed, still build the basic wrapper and keep moving. Extend the wrapper later during integration instead of marking the work blocked just because more interfaces may be needed. Fix submodule/package failures before running the top-level design. Do not add manual top-level ato.yaml entries just to build generated local package wrappers if workspace_list_targets already exposes those targets. Use the full top-level build after submodules are green; it should then be mainly an integration check rather than the first place issues appear. Check build_logs_search for errors/warnings. Use design_diagnostics for silent failures. Fix issues using Section 5 troubleshooting. Repeat until build passes cleanly. Step 6: Summary Tools: Use report_bom for parts list and report_variables for constraint summary when preparing the summary. When the build finishes, give the user a summary: What was built — list the modules, key components, and interfaces. Blockers or issues — note any problems encountered and how they were resolved (or if they remain). Suggestions for next steps — what the user might want to do next (e.g., review placement, order boards, add features, run DRC). Gate: user has received a clear summary and knows the state of the design. 1.1 Module Naming Name modules the way you'd label blocks on a system block diagram — by their role in the system , not their implementation topology. Avoid generic suffixes like Subsystem , Unit , Block , or Section . Good names: PowerSupply — input protection, regulation, and distribution PowerInput — connector, reverse polarity protection, and bulk decoupling BatteryCharger — charge IC, sense resistors, and status output BMS — cell balancing, protection, and fuel gauge GateDriver — bootstrap, dead-time, and level shifting for a FET bridge MotorDrive — integrated driver with current limit and fault output CurrentSense — shunt and sense amplifier CANTransceiver — transceiver, termination, and ESD (don't use CAN — it shadows the stdlib interface) USBPort — connector, ESD, and pull-ups (don't use USB — too generic, may shadow stdlib types) EthernetPHY — PHY, magnetics, and RJ45 Radio — RF front end, antenna match, and balun IMU — accelerometer/gyro with decoupling ADCInput — anti-alias filter, reference, and input scaling LevelShift — voltage translation between power domains InputFilter — common-mode choke and filter caps Clock — crystal or oscillator with load caps Debug — SWD/JTAG connector and pull-ups
Keywords that activate this skill. Click one to copy it.
This skill does not provide trigger words.
The downloaded .skill package contains the following fields.
| Field | Description |
|---|---|
| format | Format tag (skill/v1) |
| skill_id | Unique skill ID |
| name | Skill name |
| version | Version |
| description | Description |
| category | Categories (array) |
| trigger_words | Trigger words |
| tags | Tags |
| source | Source |
| source_url | Source URL (this page) |
| exported_at | Exported at (set per download) |
| system_prompt | System prompt body |
| model_config | Model config: provider / model / temperature / max_tokens / top_p |
| examples | Examples |
| install_guide | Import guide for Coze / Dify / Claude / custom frameworks |
The same skill can be exported in different platform formats.