This skill should be used when the user asks to "create a component", "model a part", "generate a component", "add a component", or "make a JITX component" - even without a datasheet. Also triggers on part numbers (NE555, LM1117, RP2040, etc.) and package types (SOIC, QFN, BGA, SON, SOT). Supports multi-unit symbols, thermal pads, and complex pin mappings.
Generate JITX Python component code from datasheets and specifications.
Environment setup is handled by the base jitx skill. Ensure it has been invoked first.
ALWAYS save datasheets locally before reading.
When user provides a URL or asks to download a datasheet:
datasheets/<mpn>.pdf in the project (create folder if needed)This ensures:
AVOID REDUNDANT WEB SEARCHES
Once the datasheet PDF is available, extract pinout, package dimensions, and pin descriptions from it using Step 0. Do NOT search for info that's already in the datasheet.
When additional searches ARE appropriate:
When searching:
"<MPN> datasheet" site:<manufacturer>.comALWAYS place components in a components/ folder, even for single components.
project/
└── src/<namespace>/
└── components/
├── __init__.py
├── <category>/
│ ├── __init__.py
│ └── <manufacturer>_<mpn>.py
└── <category>/
└── ...
If src/<namespace>/ doesn't exist, use:
project/
└── components/
├── __init__.py
└── <manufacturer>_<mpn>.py
Category examples: mcus, connectors, power_linear_regulators, opamp, flash, crystals, leds, logic, timers, buttons, transceivers, diodes_tvs, isolators, power_switchmode
File naming: <manufacturer>_<mpn>.py - lowercase, underscores for spaces/special chars
texas_instruments_NE555.pyraspberry_pi_RP2040.pyrenesas_DA14705.pyWhen generating a JITX component from a datasheet or specification, follow this structured approach:
NEVER read a full datasheet PDF directly. Even 50-page PDFs consume excessive context.
Always extract relevant pages first using scripts/extract_pages.py:
# Find pages containing keywords
python scripts/extract_pages.py datasheet.pdf --find "pinout" "pin description" "dimension" "package" "ball map" "mechanical"
# Extract matched pages to a smaller PDF
python scripts/extract_pages.py datasheet.pdf --pages 10 11 12 -o datasheet_extract.pdf
Then read only the extracted PDF.
Key pages to find:
If pymupdf not available, ask user to provide:
Do NOT just read the PDF and hope for the best - this will exhaust context.
IMPORTANT: Multiple Packages/Variants
If the datasheet covers multiple package options or component variants, ask the user which one to model:
Example: "The datasheet shows 3 package options for this part:
- SOIC-8 (NE555DR)
- PDIP-8 (NE555P)
- VSSOP-8 (NE555DGKR)
Which package would you like me to model?"
Do NOT assume or pick one arbitrarily. Ask first.
From the datasheet (or extracted pages), extract:
Use this decision tree to select the appropriate generator:
Is it a 2-sided package?
├── Yes, ≤6 pins → SOT23_3, SOT23_5, or SOT23_6
├── Yes, >6 pins with gull-wing leads → SOIC
├── Yes, >6 pins with flat leads (no-lead) → SON
└── No (4-sided or array)
├── 4-sided gull-wing leads → QFP
├── 4-sided flat/no-lead → QFN
├── Bottom ball array → BGA
└── Custom/unusual → Manual Landpattern
Use this template structure:
"""
{Manufacturer} {MPN} - {Description}
Component definition for the {full description}.
"""
import jitx
from jitx import PadMapping
from jitx.net import Port
from jitx.toleranced import Toleranced
from jitxlib.symbols.box import BoxSymbol, PinGroup, Row, Column
# Import appropriate landpattern generator:
# from jitxlib.landpatterns.generators.soic import SOIC, SOIC_DEFAULT_LEAD_PROFILE
# from jitxlib.landpatterns.generators.sot import SOT23_3, SOT23_5, SOT23_6, SOTLead, SOTLeadProfile
# from jitxlib.landpatterns.generators.qfn import QFN, QFNLead
# from jitxlib.landpatterns.generators.son import SON, SONLead
# from jitxlib.landpatterns.generators.bga import BGA
from jitxlib.landpatterns.leads import LeadProfile
from jitxlib.landpatterns.package import RectanglePackage
class {ComponentClassName}(jitx.Component):
"""Brief description of the component."""
mpn = "{MPN}"
manufacturer = "{Manufacturer}"
reference_designator_prefix = "U" # or "Q" for transistors, etc.
datasheet = "{datasheet_url}"
# Define ports for each pin
# Single pins:
VCC = Port()
GND = Port()
# Pin arrays (for many similar pins):
GPIO = [Port() for _ in range(N)]
# Landpattern definition
landpattern = (
{Generator}(num_leads=N)
.lead_profile(...)
.package_body(...)
# Optional: .thermal_pad(...)
)
# Symbol definition — use BARE attribute names (GND, VCC), NEVER self.GND
# (self does not exist at class scope)
symbol = BoxSymbol(
rows=Row(
left=PinGroup(...),
right=PinGroup(...),
),
columns=Column(
up=PinGroup(...), # Power pins typically go up
down=PinGroup(...), # Ground pins typically go down
),
)
# For non-standard pin ordering, add explicit mapping in __init__:
def __init__(self):
lp = self.landpattern
self.mappings = [PadMapping({
self.PIN1: [lp.p[1]],
self.PIN2: [lp.p[2]],
# ...
})]
Device: type[{ComponentClassName}] = {ComponentClassName}
For complete examples of each package type (SOIC, SOT, SON, QFN, QFP, BGA), including thermal pads, port arrays, inactive positions, and non-uniform BGA grids, see references/package-examples.md.
| Datasheet Symbol | Description | JITX Parameter |
|---|---|---|
| D | Package length | RectanglePackage.length |
| E | Package width | RectanglePackage.width |
| A | Package height | RectanglePackage.height |
| E1 / D1 | Lead span | LeadProfile.span |
| e | Lead pitch | LeadProfile.pitch |
| b | Lead width | SMDLead.width / QFNLead.width |
| L | Lead length | SMDLead.length / QFNLead.length |
| D2 / E2 | Thermal pad size | .thermal_pad(rectangle(D2, E2)) |
Ports, landpattern, and symbol defined at class level (no self). The exception to this is if a parameter is needed at the class initialization time then the definition can be done in the initialization function:
class MyIC(jitx.Component):
GND = Port() # class-level: no self
VCC = Port()
symbol = BoxSymbol(
rows=Row(
left=PinGroup(GND), # bare name, NEVER self.GND
right=PinGroup(VCC), # bare name, NEVER self.VCC
),
)
Only use self inside __init__ (for PadMapping, multi-unit symbols).
Toleranced.min_max(3.8, 4.0) # Min-max range (most common)
Toleranced(5.0, 0.1) # Nominal ± tolerance
Toleranced.min_typ_max(0.13, 0.18, 0.23) # Asymmetric
Toleranced.exact(7.0) # BSC = Basic
from jitx.shapes.composites import rectangle
from jitxlib.landpatterns.pads import SMDPadConfig, WindowSubdivide
.thermal_pad(
shape=rectangle(3.0, 3.0),
config=SMDPadConfig(paste=WindowSubdivide(padding=0.25)),
)
U - Integrated circuitsQ - Transistors (typically BJTs)D - Diodes (LEDs)R - ResistorsC - CapacitorsL - InductorsJ - ConnectorsY or X- Crystals/oscillatorsFB - Ferrite beadsT - TransformersMultiple BoxSymbol attributes = separate visual boxes:
def __init__(self):
self.symbol_a = BoxSymbol(rows=Row(
left=PinGroup(self.INp[0], self.INn[0]),
right=PinGroup(self.OUT[0]),
))
self.symbol_b = BoxSymbol(rows=Row(
left=PinGroup(self.INp[1], self.INn[1]),
right=PinGroup(self.OUT[1]),
))
# Power unit: use horizontal layout (left=supplies, right=grounds)
self.symbol_power = BoxSymbol(
rows=Row(
left=PinGroup(self.VCC, self.VBAT),
right=PinGroup(self.VSS, self.GND),
),
)
Every physical pin/ball MUST have a Port(). Never use "representative samples" — a 142-ball BGA needs exactly 142 Port() declarations. Enumerate every pin from the datasheet pin table or ball map row by row. NC (no-connect) pins with physical pads also need ports.
One Port per physical pin — if a ball has a primary name and alternate functions (SPI_CLK, UART_TX, etc.), create ONE port using the datasheet's primary pin name. Do not create separate ports for each alternate function of the same physical ball. Also for Ports that have incrementing numbers in the name, use an indexed Port name instead (GND1, GND2, GND3 -> GND[1 through 3])
Use real functional names from the datasheet, not generic placeholders:
# GOOD - from datasheet
OQSPIF_D0 = Port() # Octal QSPI Flash data bit 0
eMMC_CMD = Port() # eMMC command line
V18F = Port() # 1.8V flash supply
# BAD - generic
P0 = Port() # What does P0 do?
VDD1 = Port() # Which power domain?
Do NOT invent constructor parameters — use only these documented signatures:
# SOT — SOTLeadProfile takes ONLY span, nothing else
SOTLeadProfile(span=Toleranced.min_max(2.3, 2.5))
# LeadProfile — used for SOIC, SON, QFN
LeadProfile(
span=Toleranced.min_max(5.8, 6.2), # terminal-to-terminal
pitch=1.27, # center-to-center lead spacing
type=SONLead(length=..., width=...), # or SMDLead, QFNLead
)
# SON — use .lead_profile() method chain, NOT .lead()
SON(num_leads=8).lead_profile(LeadProfile(span=..., pitch=..., type=SONLead(...)))
# QFP — uses LeadProfile with QFPLead (BigGullWingLeads)
QFP(num_leads=48).lead_profile(LeadProfile(span=..., pitch=0.5, type=QFPLead(...)))
# For asymmetric pin counts: QFP(num_rows=(left, bottom, right, top))
# For asymmetric lead spans: .lead_profile(x_profile, y_profile)
# BGA — constructor takes these 4 args
BGA(num_rows=12, num_cols=12, pitch=0.45, ball_diameter=0.25)
# then chain: .grid_planner(...).pad_config(SMDPadConfig()).package_body(...)
.narrow() vs .package_body() for SOICSOIC provides a convenience method .narrow(length) that sets the package body to the standard SOIC narrow width (3.9mm) with a given length:
# .narrow() — shorthand for narrow-body SOIC (3.9mm width)
SOIC(num_leads=8).lead_profile(SOIC_DEFAULT_LEAD_PROFILE).narrow(Toleranced.min_max(4.81, 5.0))
# Equivalent explicit form using .package_body()
SOIC(num_leads=8).lead_profile(SOIC_DEFAULT_LEAD_PROFILE).package_body(
RectanglePackage(width=Toleranced.exact(3.9), length=Toleranced.min_max(4.81, 5.0))
)
Use .narrow() for standard narrow-body SOICs. Use .package_body() for wide-body SOICs or when specifying all three dimensions (width, length, height).
lp.thermal_pads[0])import jitx
from jitx.container import inline
from jitx.sample import SampleDesign
from .component import Device
class TestDesign(SampleDesign):
@inline
class circuit(jitx.Circuit):
dut = Device()
Always use the available virtual environment. If one is not present, stop and ask.
python -m jitx build <module>.TestDesign
Success: status: ok
Failure: Python traceback or status: error
Output files (in designs/<design_name>/):
cache/netlist.json - Verify net connectionsdesign-info/stable.design - Design snapshot| Error | Fix |
|---|---|
port X not mapped to symbol pin | Add port to BoxSymbol |
port X not mapped to pad | Check port count = pad count |
No pad configuration specified | BGA needs .pad_config(SMDPadConfig()) |
After generating code, provide:
## Verification Report
### Pin Count
- Datasheet: N pins
- Generated: N ports
- Status: ✓ MATCH / ✗ MISMATCH
### Pad Count
- Landpattern: N pads + M thermal
- Ports requiring pads: N + M
- Status: ✓ MATCH / ✗ MISMATCH
### Dimensions
| Parameter | Datasheet | Generated | Status |
|-----------|-----------|-----------|--------|
| Width | 3.8-4.0mm | min_max(3.8, 4.0) | ✓ |
### Issues Found
- [List any discrepancies or assumptions made]
After generating component code, check the datasheet for "Typical Application", "Reference Design", or "Application Circuit" sections. These provide valuable circuit templates.
When to offer:
Process:
Ask user if they want to capture the application circuit:
"The datasheet includes a Typical Application circuit (Figure X).
Would you like me to also generate the application circuit code?"
If yes, invoke the jitx-circuit-builder skill to generate circuit code
Pass context to circuit-builder:
Example application circuit output:
"""
Texas Instruments TPS62933DRLR Application Circuit
From datasheet Figure 23 - Typical Application
3.8-V to 30-V input, 3.3V 3A output buck converter.
"""
from jitx import Circuit, Net
from jitx.toleranced import Toleranced
from jitx.common import Power
from jitxlib.parts import Capacitor, CapacitorQuery, Resistor, Inductor, ResistorQuery
from jitxlib.voltage_divider import VoltageDividerConstraints, voltage_divider_from_constraints
from .texas_instruments_TPS62933DRLR import TPS62933DRLR
class TPS62933DRLRCircuit(Circuit):
"""Buck converter application circuit per datasheet Figure 23."""
vin = Power() # Input power (3.8V-30V)
vout = Power() # Output power (3.3V)
def __init__(self, output_voltage=3.3):
self.GND = Net(name="GND")
self.VOUT = Net(name="VOUT")
self.VIN = Net(name="VIN")
# Main IC
self.buck = TPS62933DRLR()
# Power connections
self.VIN += self.vin.Vp + self.buck.VIN
self.GND += self.buck.GND + self.vin.Vn + self.vout.Vn
# Input capacitors (C1, C2 - 10µF each per schematic)
with CapacitorQuery.refine(type="ceramic", case="0805"):
self.c_in1 = Capacitor(capacitance=10e-6, rated_voltage=50.0)
self.c_in1.insert(self.buck.VIN, self.GND, short_trace=True)
self.c_in2 = Capacitor(capacitance=10e-6, rated_voltage=50.0)
self.c_in2.insert(self.buck.VIN, self.GND, short_trace=True)
# Feedback voltage divider
vdiv_cons = VoltageDividerConstraints(
v_in=Toleranced.exact(output_voltage),
v_out=Toleranced.percent(0.8, 3.0), # MUST have tolerance window
current=0.8 / 10e3,
prec_series=[1.00, 0.10], # REQUIRED
base_query=ResistorQuery(case=["0402"]),
)
self.fb_div = voltage_divider_from_constraints(vdiv_cons, name="feedback")
self.VOUT += self.fb_div.hi + self.vout.Vp
self.GND += self.fb_div.lo
self.nets = [self.fb_div.out + self.buck.FB]
# Output inductor and capacitors
self.L = Inductor(inductance=4.7e-6, current_rating=3.9)
# ... complete circuit per datasheet
File location: Save application circuits alongside the component:
components/
├── power_switchmode/
│ ├── texas_instruments_TPS62933DRLR.py # Component
│ └── texas_instruments_TPS62933DRLR_circuit.py # Application circuit
When generating a component, provide: