JITX
JITX is an EDA tool that lets electrical engineers write code to design circuit boards. With JITX you can write high-level code and use our automation to design circuit boards with super-human speed, correctness and quality.
JITX works on MacOS, Linux, and Windows.
Getting Started
Installing JITX
Instructions for downloading and installing JITX.
Tutorial: Create a circuit
A tutorial covering short topics like how to find components, add them to a design, create nets, and create and use sub-circuits.
Tutorial: Schematic and Layout
A tutorial covering how to style a schematic, and complete a small layout for a power regulator.
Installation Instructions for the JITX App
This page walks you through the steps to install and log in to JITX.
Before using JITX, you must set up your JITX account -- instructions have been sent to the email address that was used to sign-up. If you haven't received the email, please check your spam folder.
JITX is shipped as an extension for VSCode. Therefore, we first install VSCode and then add the JITX extension for VSCode.
Steps:
- Install Visual Studio Code
- Install the JITX VSCode Extension
- Open the sidebar
- Login to JITX
- Quickstart Video Series
1 Install Visual Studio Code
VSCode is an integrated development environment (IDE) which integrates tools for development including an editor, compiler, and source control. The JITX compiler and UI are integrated with VSCode. To install VSCode:
- MacOS
- Linux (For Ubuntu use:
sudo apt-get install code
) - Windows (can also install from Microsoft store)
2 Install the JITX VSCode Extension
Method
Click the Extensions
icon in the Activity Bar, search for "jitx
" and you will see the following listing:
Click the Install
button to start the installation process.
3 Open the sidebar
Click on the JITX icon in the left hand activity bar to open the JITX sidebar.
4 Login to JITX
Click the JITX
icon in the Activity Bar and sign in with the username and password you created using the link from the welcome email.
5 Quickstart Videos
Tutorial: Create a circuit
NOTE: Before starting this tutorial, you should have installed and set up JITX.
You can follow along with this tutorial in the video above. Below is a transcript to make the code and text easier to read.
1. Searching for components
Open the JITX sidebar and click on Find Components. Search for:
TPS62080DSGR
Then click on the "Create Component" button. This adds a local code model of a component to your project.
To add it to your design, in main.stanza, change the my-design
module to be:
pcb-module my-design:
inst buck : components/Texas-Instruments/TPS62080DSGR/component
You have added this component to your design, run it to see it appear in the schematic and layout.
2. JITX component models
Open the components/Texas-Instruments/TPS62080DSGR.stanza
file.
Delete the original pcb-symbol
definition. In the pcb-component
definition, shuffle the order of the pins, and replace the assign-symbol()
line so that the new component body becomes:
pin-properties :
[pin:Ref | pads:Ref ... | side:Dir | electrical-type:String | bank:Int]
[VIN | p[8] | Left | "power_in" | 0]
[EN | p[1] | Left | "unspecified" | 0]
[MODE | p[3] | Left | "unspecified" | 0]
[GND | p[2] p[9] | Left | "power_in" | 0]
[PG | p[6] | Right | "unspecified" | 0]
[SW | p[7] | Right | "power_in" | 0]
[VOS | p[5] | Right | "unspecified" | 0]
[FB | p[4] | Right | "unspecified" | 0]
assign-landpattern(lp)
make-box-symbol()
3. Reusable circuits
Below the component definition in TPS62080DSGR.stanza
, add:
public pcb-module module :
inst reg : components/Texas-Instruments/TPS62080DSGR/component
4. Using models from the database
Open the JITX sidebar and click on Find Components. Search for:
NR3015T1R0N
Then click on the "Copy Component" button, and paste it into the module:
public pcb-module module (-- output-voltage:Double = 3.3) :
inst buck : components/Texas-Instruments/TPS62080DSGR/component
inst L : database-part(["mpn" => "NR3015T1R0N", "manufacturer" => "Taiyo Yuden"])
To use the database-part
function, you'll have to import generic-components
at the top:
defpackage components/Texas-Instruments/TPS62080DSGR :
import core
import jitx
import jitx/commands
import ocdb/utils/box-symbol
import ocdb/utils/generic-components
Change main.stanza
to use the new module:
pcb-module my-design:
inst buck : components/Texas-Instruments/TPS62080DSGR/module
5. Making connections with net
In the module definition in TPS62080DSGR.stanza
, add ports and nets to build the circuit and expose an interface:
public pcb-module module (-- output-voltage:Double = 3.3) :
port vin
port vout
port en
port gnd
inst buck : components/Texas-Instruments/TPS62080DSGR/component
inst L : database-part(["mpn" => "NR3015T1R0N", "manufacturer" => "Taiyo Yuden"])
net (buck.SW L.p[1])
net (buck.VOS L.p[2] vout)
net (buck.VIN vin)
net (buck.EN en)
net (buck.GND gnd buck.MODE)
In main.stanza
you can now net the module pins:
pcb-module my-design:
inst buck : components/Texas-Instruments/TPS62080DSGR/module
net (buck.vin buck.en)
6. Parametric components
In the module definition in TPS62080DSGR.stanza
, add the input and output capacitors:
bypass-cap-strap(buck.VIN, buck.GND, 10.0e-6)
bypass-cap-strap(L.p[2], buck.GND, 22.0e-6)
7. Reusing a smart sub-circuit
In the module definition in TPS62080DSGR.stanza
, add the voltage divider and attach it:
inst feedback : ocdb/modules/passive-circuits/voltage-divider(
source-voltage = typ(3.3),
divider-output = 0.45 +/- (3 %),
current = 100.0 * 100.0e-9)
net (feedback.in L.p[2])
net (feedback.out buck.FB)
net (feedback.lo buck.GND)
8. Making the parametric power regulator
Finally, change the module definition in TPS62080DSGR.stanza
to accept an output-voltage
argument, and pass that in to the voltage divider. Here is the completed module:
public pcb-module module (-- output-voltage:Double = 3.3) :
port vin
port vout
port en
port gnd
inst buck : components/Texas-Instruments/TPS62080DSGR/component
inst L : database-part(["mpn" => "NR3015T1R0N", "manufacturer" => "Taiyo Yuden"])
net (buck.SW L.p[1])
net (buck.VOS L.p[2] vout)
net (buck.VIN vin)
net (buck.EN en)
net (buck.GND gnd buck.MODE)
bypass-cap-strap(buck.VIN, buck.GND, 10.0e-6)
bypass-cap-strap(L.p[2], buck.GND, 22.0e-6)
inst feedback : ocdb/modules/passive-circuits/voltage-divider(
source-voltage = typ(output-voltage),
divider-output = 0.45 +/- (3 %),
current = 100.0 * 100.0e-9)
net (feedback.in L.p[2])
net (feedback.out buck.FB)
net (feedback.lo buck.GND)
Change main.stanza
you can now net the module pins:
pcb-module my-design:
inst buck : components/Texas-Instruments/TPS62080DSGR/module(output-voltage = 3.3)
net (buck.vin buck.en)
Next: Tutorial: Schematic and layout
Completed code for Tutorial: Create a circuit
; This file is generated based on the parts database query below:")
; database-part(["manufacturer" => "Texas Instruments", "mpn" => "TPS62080DSGR"])
#use-added-syntax(jitx)
defpackage components/Texas-Instruments/TPS62080DSGR :
import core
import jitx
import jitx/commands
import ocdb/utils/box-symbol
import ocdb/utils/generic-components
pcb-pad rectangle-smd-pad :
name = "rectangle-smd-pad"
type = SMD
shape = Rectangle(0.280, 0.505)
layer(SolderMask(Top)) = Rectangle(0.382, 0.607)
layer(Paste(Top)) = Rectangle(0.382, 0.607)
pcb-pad rectangle-smd-pad-1 :
name = "rectangle-smd-pad-1"
type = SMD
shape = Rectangle(1.600, 0.900)
layer(SolderMask(Top)) = Rectangle(1.702, 1.002)
layer(Paste(Top)) = Rectangle(1.702, 1.002)
pcb-landpattern lp :
pad p[1] : rectangle-smd-pad at loc(-0.750, -0.928) on Top
pad p[2] : rectangle-smd-pad at loc(-0.250, -0.928) on Top
pad p[3] : rectangle-smd-pad at loc(0.250, -0.928) on Top
pad p[4] : rectangle-smd-pad at loc(0.750, -0.928) on Top
pad p[5] : rectangle-smd-pad at loc(0.750, 0.928) on Top
pad p[6] : rectangle-smd-pad at loc(0.250, 0.928) on Top
pad p[7] : rectangle-smd-pad at loc(-0.250, 0.928) on Top
pad p[8] : rectangle-smd-pad at loc(-0.750, 0.928) on Top
pad p[9] : rectangle-smd-pad-1 at loc(0.0, -0.000499999999988177) on Top
layer(Silkscreen("F-SilkS", Top)) = Text(">REF", 0.5, W, loc(-0.750, 2.937), "", TrueTypeFont, false, false)
layer(CustomLayer("Fab", Top)) = Text(">VALUE", 0.5, W, loc(-0.750, 1.937), "", TrueTypeFont, false, false)
layer(Silkscreen("F-SilkS", Top)) = Line(0.152, [Point(1.076, 1.077), Point(1.076, -1.077)])
layer(Silkscreen("F-SilkS", Top)) = Line(0.152, [Point(-1.076, -1.077), Point(-1.076, 1.077)])
layer(CustomLayer("Fab", Top)) = Polyline(0.060, [
Arc(-1.000, -1.000, 0.030, 0.0, -360.000)])
layer(CustomLayer("Fab", Top)) = Polyline(0.300, [
Arc(-0.762, -1.271, 0.150, 0.0, -360.000)])
layer(Silkscreen("F-SilkS", Top)) = Polyline(0.200, [
Arc(-0.750, -1.481, 0.100, 0.0, -360.000)])
layer(Courtyard(Top)) = Rectangle(2.304, 2.462)
model3d = Model3D("../../3d-models/jitx-64d12e58b789d8dc4b77c71a.stp",
Vec3D(0.0, 0.0, 0.0),
Vec3D(1.000, 1.000, 1.000),
Vec3D(0.0, 0.0, 0.0),
[],
)
public pcb-component component :
name = "C130071"
description = "Step-down type Adjustable 0.5V~4V 1.2A 2.3V~6V DFN-8-EP(2x2) DC-DC Converters ROHS"
manufacturer = "Texas Instruments"
mpn = "TPS62080DSGR"
datasheet = "https://datasheet.lcsc.com/lcsc/1806051415_Texas-Instruments-TPS62080DSGR_C130071.pdf"
reference-prefix = "U"
pin-properties :
[pin:Ref | pads:Ref ... | side:Dir | electrical-type:String | bank:Int]
[VIN | p[8] | Left | "power_in" | 0]
[EN | p[1] | Left | "unspecified" | 0]
[MODE | p[3] | Left | "unspecified" | 0]
[GND | p[2] p[9] | Left | "power_in" | 0]
[PG | p[6] | Right | "unspecified" | 0]
[SW | p[7] | Right | "power_in" | 0]
[VOS | p[5] | Right | "unspecified" | 0]
[FB | p[4] | Right | "unspecified" | 0]
assign-landpattern(lp)
make-box-symbol()
property(self.category) = "ic"
property(self.manufacturer_aliases) = ["Texas Instruments"]
property(self.mpn_aliases) = ["TPS62080DSGR"]
property(self.cofactr_id) = "IC8WHGVOE6M1"
property(self.reference_prefix) = "U"
property(self.trust) = "low"
property(self.x) = 2.304
property(self.y) = 2.462
property(self.area) = 5.672
property(self.case) = "DFN-8(2x2)"
property(self.mounting) = "smd"
public pcb-module module (-- output-voltage:Double = 3.3) :
port vin
port vout
port en
port gnd
inst buck : components/Texas-Instruments/TPS62080DSGR/component
inst L : database-part(["mpn" => "NR3015T1R0N", "manufacturer" => "Taiyo Yuden"])
net (buck.SW L.p[1])
net (buck.VOS L.p[2] vout)
net (buck.VIN vin)
net (buck.EN en)
net (buck.GND gnd buck.MODE)
bypass-cap-strap(buck.VIN, buck.GND, 10.0e-6)
bypass-cap-strap(L.p[2], buck.GND, 22.0e-6)
inst feedback : ocdb/modules/passive-circuits/voltage-divider(
source-voltage = typ(output-voltage),
divider-output = 0.45 +/- (3 %),
current = 100.0 * 100.0e-9)
net (feedback.in L.p[2])
net (feedback.out buck.FB)
net (feedback.lo buck.GND)
Tutorial: Schematic and layout
NOTE: Before starting this tutorial, you should have built the design from Tutorial: Create a circuit
You can follow along with this tutorial in the video above. Below is a transcript to make the code and text easier to read.
Naming nets and adding power symbols
In main.stanza
, name the net attached to vin
of the buck regulator module VIN
:
pcb-module my-design :
inst buck : components/Texas-Instruments/TPS62080DSGR/module(output-voltage = 3.3)
net VIN (buck.vin buck.en)
Run the design to see the VIN
label appear on the schematic.
Next, create a net GND
for ground, and assign a net symbol to it:
pcb-module my-design :
inst buck : components/Texas-Instruments/TPS62080DSGR/module(output-voltage = 3.3)
net VIN (buck.vin buck.en)
net GND (buck.gnd)
symbol(GND) = ocdb/utils/symbols/ground-sym
Schematic
The schematic UI is for changing the style of the schematic. Groups are automatically added based on the structure of the pcb-modules in your design.
All commands can be discovered from the (?) button in the upper-right of the screen.
Layout
The layout UI is for completing the physical layout of your circuit board. You select which signals will be routed on which layer, and then route them with the autorouter. You can move components around, and the traces will follow.
You can use Adaptive
for low inductance and low resistance routes, and generate pours automatically from dynamic traces.
You decide which pads need vias, and there is an auto-via tool to place and route those vias automatically. Vias are associated with those pads, and when you fan out a component those vias will travel with the component.
All commands can be discovered from the (?) button in the upper-right of the screen.
Completed Code for the Tutorial: Schematic and Layout Tutorial
; This file is generated based on the parts database query below:")
; database-part(["manufacturer" => "Texas Instruments", "mpn" => "TPS62080DSGR"])
#use-added-syntax(jitx)
defpackage components/Texas-Instruments/TPS62080DSGR :
import core
import jitx
import jitx/commands
import ocdb/utils/box-symbol
import ocdb/utils/generic-components
pcb-pad rectangle-smd-pad :
name = "rectangle-smd-pad"
type = SMD
shape = Rectangle(0.280, 0.505)
layer(SolderMask(Top)) = Rectangle(0.382, 0.607)
layer(Paste(Top)) = Rectangle(0.382, 0.607)
pcb-pad rectangle-smd-pad-1 :
name = "rectangle-smd-pad-1"
type = SMD
shape = Rectangle(1.600, 0.900)
layer(SolderMask(Top)) = Rectangle(1.702, 1.002)
layer(Paste(Top)) = Rectangle(1.702, 1.002)
pcb-landpattern lp :
pad p[1] : rectangle-smd-pad at loc(-0.750, -0.928) on Top
pad p[2] : rectangle-smd-pad at loc(-0.250, -0.928) on Top
pad p[3] : rectangle-smd-pad at loc(0.250, -0.928) on Top
pad p[4] : rectangle-smd-pad at loc(0.750, -0.928) on Top
pad p[5] : rectangle-smd-pad at loc(0.750, 0.928) on Top
pad p[6] : rectangle-smd-pad at loc(0.250, 0.928) on Top
pad p[7] : rectangle-smd-pad at loc(-0.250, 0.928) on Top
pad p[8] : rectangle-smd-pad at loc(-0.750, 0.928) on Top
pad p[9] : rectangle-smd-pad-1 at loc(0.0, -0.000499999999988177) on Top
layer(Silkscreen("F-SilkS", Top)) = Text(">REF", 0.5, W, loc(-0.750, 2.937), "", TrueTypeFont, false, false)
layer(CustomLayer("Fab", Top)) = Text(">VALUE", 0.5, W, loc(-0.750, 1.937), "", TrueTypeFont, false, false)
layer(Silkscreen("F-SilkS", Top)) = Line(0.152, [Point(1.076, 1.077), Point(1.076, -1.077)])
layer(Silkscreen("F-SilkS", Top)) = Line(0.152, [Point(-1.076, -1.077), Point(-1.076, 1.077)])
layer(CustomLayer("Fab", Top)) = Polyline(0.060, [
Arc(-1.000, -1.000, 0.030, 0.0, -360.000)])
layer(CustomLayer("Fab", Top)) = Polyline(0.300, [
Arc(-0.762, -1.271, 0.150, 0.0, -360.000)])
layer(Silkscreen("F-SilkS", Top)) = Polyline(0.200, [
Arc(-0.750, -1.481, 0.100, 0.0, -360.000)])
layer(Courtyard(Top)) = Rectangle(2.304, 2.462)
model3d = Model3D("../../3d-models/jitx-64d12e58b789d8dc4b77c71a.stp",
Vec3D(0.0, 0.0, 0.0),
Vec3D(1.000, 1.000, 1.000),
Vec3D(0.0, 0.0, 0.0),
[],
)
public pcb-component component :
name = "C130071"
description = "Step-down type Adjustable 0.5V~4V 1.2A 2.3V~6V DFN-8-EP(2x2) DC-DC Converters ROHS"
manufacturer = "Texas Instruments"
mpn = "TPS62080DSGR"
datasheet = "https://datasheet.lcsc.com/lcsc/1806051415_Texas-Instruments-TPS62080DSGR_C130071.pdf"
reference-prefix = "U"
pin-properties :
[pin:Ref | pads:Ref ... | side:Dir | electrical-type:String | bank:Int]
[VIN | p[8] | Left | "power_in" | 0]
[EN | p[1] | Left | "unspecified" | 0]
[MODE | p[3] | Left | "unspecified" | 0]
[GND | p[2] p[9] | Left | "power_in" | 0]
[PG | p[6] | Right | "unspecified" | 0]
[SW | p[7] | Right | "power_in" | 0]
[VOS | p[5] | Right | "unspecified" | 0]
[FB | p[4] | Right | "unspecified" | 0]
assign-landpattern(lp)
make-box-symbol()
property(self.category) = "ic"
property(self.manufacturer_aliases) = ["Texas Instruments"]
property(self.mpn_aliases) = ["TPS62080DSGR"]
property(self.cofactr_id) = "IC8WHGVOE6M1"
property(self.reference_prefix) = "U"
property(self.trust) = "low"
property(self.x) = 2.304
property(self.y) = 2.462
property(self.area) = 5.672
property(self.case) = "DFN-8(2x2)"
property(self.mounting) = "smd"
public pcb-module module (-- output-voltage:Double = 3.3) :
port vin
port vout
port en
port gnd
inst buck : components/Texas-Instruments/TPS62080DSGR/component
inst L : database-part(["mpn" => "NR3015T1R0N", "manufacturer" => "Taiyo Yuden"])
net (buck.SW L.p[1])
net (buck.VOS L.p[2] vout)
net (buck.VIN vin)
net (buck.EN en)
net (buck.GND gnd buck.MODE)
bypass-cap-strap(buck.VIN, buck.GND, 10.0e-6)
bypass-cap-strap(L.p[2], buck.GND, 22.0e-6)
inst feedback : ocdb/modules/passive-circuits/voltage-divider(
source-voltage = typ(output-voltage),
divider-output = 0.45 +/- (3 %),
current = 100.0 * 100.0e-9)
net (feedback.in L.p[2])
net (feedback.out buck.FB)
net (feedback.lo buck.GND)
JITX Reference
Reference materials refer to technical descriptions of JITX’s machinery and its operation. Things like descriptions of key classes, functions, and APIs would fall under this umbrella. This includes types, statements, and commands among other things.
- Types are classifications of circuit board concepts and define their possible values and operations.
- Statements are pieces of code, often one line each, that together define a design. Statements are what constructs ESIR (Electronic Systems Intermediate Representation) to create designs.
- Commands are function calls that retrieve information from or modify a design.
Examples
- Commands
pins (obj:JITXObject)
retrieves all pins in the given JITXObject.set-rules (rules:Rules)
assigns aRules
to your design.
- Statements
mpn = "TRS3122ERGER"
assigns a component's mpn.port p : pin[2]
creates a port with two pins.
- Types
- Top-level definitions have types including
LandPattern
,SchematicSymbol
, andBoard
. They all subtypeJITXDef
, meaning that objects with these types can perform the same operations as aJITXDef
. - Local definitions have types including
LandPatternPad
andSymbolPin
. They all subtypeJITXObject
.
- Top-level definitions have types including
JITX Statements
Statement | Description |
---|---|
pcb-board | Metadata of the board itself |
pcb-bundle | A collection of other top level statements |
pcb-check | A check on the correctness of our design |
pcb-component | A single device such as an IC or passive element. |
pcb-enum | A store for categorical variables |
pcb-landpattern | A component footprint/land pad. |
pcb-material | A material for a layer in the stackup |
pcb-module | A single reusable module which may contain other top level statements. |
pcb-pad | A single pad on the physical device/footprint |
pcb-rules | Fab house rules (tolerances, trace widths, |
pcb-routing-structure | Single-Ended Routing Structures |
pcb-differential-routing-structure | Differential Routing Structures |
pcb-stackup | The stackup for the board |
pcb-via | The via definition and parameters |
pcb-struct | A way to group several related variables. |
pcb-symbol | A schematic symbol |
Boards
A pcb-board
statement defines the physical structure of the circuit board for a design. A board contains a stackup, boundary, signal boundary, and optional layer statements.
Signature
pcb-board board-name (arg1:Type1, ...):
name = <String|False>
description = <String|False>
boundary = <Shape>
signal-boundary = <Shape|False>
stackup = <Stackup>
vias = [<Via>, <Via>, ... ]
layer(<LayerSpecifier>) = <Shape>
...
The expression name board-name
uniquely identifies this board definition in the current context.
The argument list (arg1:Type1, ...)
is optional and provides a means of constructing parameterized board definitions.
- Required Parameters
boundary
- This is aShape
that defines the physical board outline.- This shape must be a simple polygon.
- This means that it must not have any holes or self-intersections.
stackup
- User must provide a pcb-stackup definition.- This defines the copper and dielectric layer stackup.
vias
- Tuple of pcb-via definitions.- These definitions limit what vias are allowed to be placed on the board.
- Only the vias present in this tuple will populate the drop-down via menu in the board tool.
- Optional Parameters
name
- Optional string to provide a human readable name, mostly for the UI. If not present, then the expression name will be used.description
- Optional string to describe this object. This is primarily for documentation and use in the UI.signal-boundary
- This is an optionalShape
that defines what region of the board the router is allowed to route copper.- This shape must be a simple polygon.
- If not provided, the board definition will re-use the
boundary
shape for this parameter. - If provided, this shape must be no larger than the
boundary
shape. If this shape exceeds theboundary
shape, then the JITX runtime will raise an error.
layer()
- Optional layer statements that allow the user to introduce geometry on any of the layers of the board.
Usage
The pcb-board
definition is passed to the JITX runtime via the set-board function.
val board-shape = Rectangle(60.0, 40.0)
val my-stackup = ocdb/manufacturers/stackups/jlcpcb-jlc2313
pcb-via default-th:
start = Top
stop = Bottom
diameter = 0.65
hole-diameter = 0.4
type = MechanicalDrill
tented = true
pcb-board my-circuit-board :
stackup = my-stackup
boundary = board-shape
signal-boundary = offset(board-shape, -0.5)
vias = [default-th]
...
set-board(my-circuit-board)
The OCDB library has many predefined stackups that can be useful for building prototypes.
Notice the use of the offset function to construct a signal-boundary
that is slightly inset from the board boundary
.
Using Arguments
Parameterized board definitions can be useful when constructing common structures. Building on the previous example:
val STD-FIXTURE-SHAPE = RoundedRectangle(100.0, 40.0, 1.0)
pcb-board fixture-board (
base-stackup:Stackup,
--
outline:Shape = STD-FIXTURE-SHAPE,
sig-buffer:Double = 0.5
) :
name = to-string("fixture-%_-%_" % [outline, sig-buffer])
stackup = base-stackup
boundary = outline
signal-boundary = offset(outline, (- sig-buffer))
vias = [default-th]
...
val my-stackup = ocdb/manufacturers/stackups/jlcpcb-jlc2313
; Example #1
val brd = fixture-board(my-stackup)
; Example #2
val brd = fixture-board(my-stackup, sig-buffer = 1.0)
set-board( brd )
In this example, we demonstrate the use of optional keyword arguments for parametrically defining the board for our design.
Here the sig-buffer
argument is a keyword argument with a default value of 0.5
. This forces the signal-boundary
to be 0.5mm smaller than the boundary
shape on all sides
of the board.
Making it a keyword argument provides better readability of the code - we don't have to wonder whether that second argument was supposed to be the outline
shape or the sig-buffer
.
It is often convenient to include a name
constructed from the parameters so that we can differentiate two different pcb-board
definitions constructed with this function.
Using Generators
Generators are a tool to compose reusable features into a definition.
defn set-boundary (outline:Shape -- sig-buffer:Double = 0.5) :
inside pcb-board:
boundary = outline
signal-boundary = offset(outline, (- sig-buffer))
val board-shape = Rectangle(60.0, 40.0)
pcb-board circuit-board-2 :
stackup = my-stackup
vias = [default-th]
set-boundary(board-shape)
...
set-board(circuit-board-2)
The set-boundary
function is a generator. Notice the use of the inside pcb-board
syntax. When set-boundary
is called from within the context of a pcb-board
definition, it can create any of the expected pcb-board
parameters. In this particular instance, the set-boundary
function sets the boundary
and signal-boundary
parameters.
The resulting circuit-board-2
definition would be equivalent to:
val board-shape = Rectangle(60.0, 40.0)
pcb-board equivalent-circuit-board :
stackup = my-stackup
vias = [default-th]
boundary = board-shape
signal-boundary = offset(board-shape, -0.5)
Calling set-boundary
from any other context (ie, a regular function or a pcb-module
) will elicit an error because the boundary =
syntax will be unknown.
Name
The name
statement is the optional name field of a JITX object. Use it to store a descriptive name as a String
. This name will often be used in the UI in place of the object's expression name for better readability.
Signature
name = <String>
This statement is optional. The default value will be the definition's symbol name in case no name
statement is found in a definition.
Any string is a valid name
value.
Usage
Literal String Names
pcb-component component :
name = "ADM7150"
pcb-module band-pass-filter :
name = "Band-pass filter"
The examples name = "ADM7150"
and name = "Band-pass filter"
use a String
literal for the name.
Formatted Strings
pcb-pad smd-pad (anchor:Anchor, w:Double, h:Double) :
name = to-string("%_x%_ %_ SMD Pad" % [w,h,anchor])
pcb-landpattern test-lp:
pad p[1] : smd-pad(C, 0.6, 0.7) at loc(x0,y0)
We can also construct strings using formatting routes and parameter arguments for a particular definition. In this example, the constructed smd-pad
name property would be 0.6x0.7 C SMD Pad
Description
The description
statement is the optional descriptive field of a JITX object. Use it to store a description of the object for human designers reading JITX, and to make the object easier to find via text search. This description also shows up in the UI, such as the design explorer, to provide more insight into particular components and modules in the design.
Signature
description = <String|False>
This statement is optional. The default value will be false
in case no description
statement is found in a definition.
Any string is a valid description
value.
Each JITX definition may have exactly one description
statement. If more than one statement is encountered in a definition, then a DuplicateCStmtError
exception will be raised.
Examples
; We can use string literals to describe a particular component.
pcb-component analog-devices-ADM7150 :
description = "800 mA Ultralow Noise, High PSRR, RF Linear Regulator"
; We can use string formatting to construct descriptions based on variables
; or arguments to a JITX Definition
pcb-module band-pass-filter (high-cut:Double, low-cut:Double) :
description = to-string(
"Band-pass Filter - Highpass = %_ Hz and Lowpass = %_ Hz." % [high-cut, low-cut]
)
Layer
The layer
statement is used to create geometry on the non-copper layers of a circuit board. The layer()
statement is valid in the following contexts:
Signature
layer(<LayerSpecifier>) = <Shape>
<LayerSpecifier>
- A LayerSpecifier instance that identifies which non-copper layer to apply the provided geometry to.<Shape>
- AShape
instance that defines the geometry that will be created on the specified layer.
Usage
The most common usage of the layer()
statement is in pcb-landpattern:
pcb-landpattern diode-lp :
pad c : smd-pad(0.4, 0.75) at loc(-1.25, 0.0) on Top
pad a : smd-pad(0.4, 0.75) at loc(1.25, 0.0) on Top
layer(Silkscreen("body", Top)) = LineRectangle(1.8, 1.0)
layer(Silkscreen("body", Top)) = Line(0.1, [Point(-0.70, -0.5), Point(-0.70, 0.5)])
layer(Courtyard(Top)) = Rectangle(3.2, 2.0)
layer(ForbidCopper(LayerIndex(0))) = Rectangle(2.0, 1.0)
This will construct a landpattern that looks like this:
Notice the silkscreen in yellow with cathode marker. The blue box is the ForbidCopper
layer on the Top Layer. Red is the top copper pads for the cathode c
and anode a
.
The white bounding rectangle is the Courtyard
layer.
See LayerSpecifier for more information about specific layers.
Cutouts
When constructing cutouts in the board layout, your best bet is to use a solid region as opposed to a line. A line can confuse the routing engine into thinking that there are two physically separate regions where copper can be placed.
Consider a USB connector part, U231-096N-3BLRT06-SS, Jing Extension of the Electronic Co.
Here is an excerpt from the datasheet:
If we draw the cutout with a line, as shown in the datasheet, we get this:
pcb-landpattern USB-conn:
...
layer(Cutout()) = Line(0.254, [Point(-7.300, -7.650), Point(-8.450, -7.650)])
layer(Cutout()) = Polyline(0.254, [
Point(6.850, 4.650)
Point(6.850, -7.650)
Point(8.450, -7.650)])
layer(Cutout()) = Polyline(0.254, [
Point(-6.850, 4.650)
Point(-6.850, -7.650)
Point(-7.300, -7.650)])
layer(Cutout()) = Line(0.254, [Point(-6.850, 4.650), Point(6.850, 4.650)])
The cutout line is in gold color. Notice that the ground layer (blue) copper is present on both sides of the cut line with some margin in between. The routing engine basically thinks that the cutout is just the line. If we were making a slot - that would probably be reasonable. But for this case, we want the hole region between the cutout line and the board edge (red) to be a cutout region.
The right way is to use a Rectangle
or Polygon
solid geometry:
pcb-landpattern USB-conn :
...
layer(Cutout()) = Polygon([
Point(-6.850, 4.650), Point(6.850, 4.650),
Point(6.850, -7.650), Point(-6.850, -7.65),
])
layer(Cutout()) = Circle(Point(-6.85 + 0.5, 4.65), 0.5)
layer(Cutout()) = Circle(Point(6.85 - 0.5, 4.65), 0.5)
Notice that the cutout region fills the entire connector region and the blue ground plane is not present in the cutout region.
Bundle Statements
A pcb-bundle
definition groups a set of pins. Bundles can include ports and other bundles. You can connect bundles using a net
statement, or use supports
and require
statements with bundles for automated pin assignment.
For example we can create a bundle for an i2c bus with the following statement:
pcb-bundle i2c:
port sda
port scl
Bundles can be parametric and include other bundles. Here is a definition for a bank of lvds pins of variable size:
pcb-bundle diff-pair :
port N
port P
pcb-bundle lvds-bank (width:Int) :
port clk : diff-pair
port data : diff-pair[width]
You can make bundles parametric to represent optional pins. Here's an example of a parametric SWD bundle, that can optionally include the SWO pin:
pcb-bundle swd (swo:True|False) :
port swdio
port swclk
port reset
if swo :
port swo
Statements
Here is the list of all of the statements you can use in a pcb-bundle
:
Statement | Description |
---|---|
name | Name of the bundle |
description | Description for the bundle |
ports | Creates elements you can connect to |
Name
The name
statement is the optional name field of a JITX object. Use it to store a descriptive name as a String
. This name will often be used in the UI in place of the object's expression name for better readability.
Signature
name = <String>
This statement is optional. The default value will be the definition's symbol name in case no name
statement is found in a definition.
Any string is a valid name
value.
Usage
Literal String Names
pcb-component component :
name = "ADM7150"
pcb-module band-pass-filter :
name = "Band-pass filter"
The examples name = "ADM7150"
and name = "Band-pass filter"
use a String
literal for the name.
Formatted Strings
pcb-pad smd-pad (anchor:Anchor, w:Double, h:Double) :
name = to-string("%_x%_ %_ SMD Pad" % [w,h,anchor])
pcb-landpattern test-lp:
pad p[1] : smd-pad(C, 0.6, 0.7) at loc(x0,y0)
We can also construct strings using formatting routes and parameter arguments for a particular definition. In this example, the constructed smd-pad
name property would be 0.6x0.7 C SMD Pad
Description
The description
statement is the optional descriptive field of a JITX object. Use it to store a description of the object for human designers reading JITX, and to make the object easier to find via text search. This description also shows up in the UI, such as the design explorer, to provide more insight into particular components and modules in the design.
Signature
description = <String|False>
This statement is optional. The default value will be false
in case no description
statement is found in a definition.
Any string is a valid description
value.
Each JITX definition may have exactly one description
statement. If more than one statement is encountered in a definition, then a DuplicateCStmtError
exception will be raised.
Examples
; We can use string literals to describe a particular component.
pcb-component analog-devices-ADM7150 :
description = "800 mA Ultralow Noise, High PSRR, RF Linear Regulator"
; We can use string formatting to construct descriptions based on variables
; or arguments to a JITX Definition
pcb-module band-pass-filter (high-cut:Double, low-cut:Double) :
description = to-string(
"Band-pass Filter - Highpass = %_ Hz and Lowpass = %_ Hz." % [high-cut, low-cut]
)
Ports
port
is a JITX statement that defines the electrical connection points for a component or module.
The port statement can be used in the following contexts:
Each port
statement in any of these contexts is public
by default. This means that each port can be accessed externally by using dot
notation.
Signature
; Single Port Instance
port <NAME> : <TYPE>
; Array Port Instance
port <NAME> : <TYPE>[<ARRAY:Int|Tuple>]
<NAME>
- Symbol name for the port in this context. This name must be unique in the current context.<TYPE>
- The type of port to construct. This can be the keywordpin
for a single pin type or any declaredpcb-bundle
type.<ARRAY:Int|Tuple>
- Optional array initializer argument. This value can be:Int
-PortArray
constructed with lengthARRAY
. This array is constructed as a contiguous zero-index array.Tuple
-PortArray
constructed with an explicit set of indices. This array is not guaranteed to be contiguous.
Watch Out! - There is no space between
<TYPE>
and the[
opening bracket of the array initializer.
Syntax
There are multiple forms of the port
statement to allow for flexible construction of the interface to a particular component. The following sections outline these structures.
Basic Port Declaration
The user has two options when declaring a single pin:
; Single Pin 'a'
port a : pin
; Equivalent to
port a
The port a
structure is a shorthand version for declaring single pins.
Pin Arrays
To construct a bus of single pins, we use the array constructor:
port contiguous-bus : pin[6]
port non-contiguous-bus : pin[ [2, 5, 6] ]
println(indices(non-contiguous-bus))
; Prints:
; [2 5 6]
The contiguous-bus
port is defined as a contiguous array of pins with indices: 0, 1, 2, 3, 4, & 5.
The non-contiguous-bus
port is defined an array with explicit indices: 2, 5, & 6. The indices
function is a helper function for determining what array indices are available on this port.
Attempting to access a port index that does not exist or is negative will result in an error.
Bundle Port
A bundle port is a connection point that consolidates multiple individual signals into one entity. The structure of this port is defined by the pcb-bundle
type used in its declaration:
pcb-bundle i2c:
port sda
port scl
pcb-module mcu:
port data : i2c
In this example, we define the i2c
bundle. Notice that this bundle is constructed from same port
statements defined above.
The data
port is then constructed with the bundle name i2c
as the type.
The individual signals of the data
port are accessible using dot
notation:
pcb-module top-level :
inst U1 : mcu
println(port-type(mcu.data.sda))
; Prints:
; [SinglePin object]
Bundle Array Port
As with the single pin, we can construct port arrays of bundle types:
pcb-bundle i2c:
port sda
port scl
pcb-module mcu:
port busses : i2c[3]
In this example, we construct a contiguous array of 3 I2C bus ports. We can access the individual signals of these busses as well:
pcb-module top-level :
inst U1 : mcu
println(port-type(mcu.busses[0].scl))
; Prints:
; [SinglePin object]
Port Types
Each port has a type associated with it. That type can be accessed with the function port-type. This function returns a PortType
instance that can be one of the following types:
SinglePin
- A port instance of typepin
Bundle
- A port instance of type BundlePortArray
- An array of port instances
We would typically use the result of this function with a match statement:
pcb-module amp:
port vout : pin[4]
match(port-type(vout)):
(s:SinglePin): println("Single")
(b:Bundle): println("Bundle")
(a:PortArray): println("Array")
This structure allows a different response depending on the type of port.
The Bundle
PortType
The Bundle
port type provides the user with access to the type of bundle that was used to construct this port:
pcb-bundle i2c:
port sda
port scl
pcb-module mcu:
port bus : i2c
pcb-module top-level:
inst U1 : mcu
match(port-type(U1.bus)):
(b:Bundle):
println("Bundle Type: %_" % [name(b)])
(x): println("Other")
; Prints:
; Bundle Type: i2c
Notice that the b
object is a reference to the pcb-bundle i2c
definition. This provides a convenient way to check if a given port matches a particular bundle type.
Walking a PortArray
It is often useful to walk a PortArray
instance and perform some operation on each of the SinglePin
instances of that PortArray
. Because PortArray
instances can be constructed from either single pins or bundles, they can form arbitrarily complex trees of signals. To work with trees of this nature, recursion is the tool of choice.
Note: This is a more advanced example with recursion. Fear not brave electro-adventurer - These examples will serve you well as you become more comfortable with the JITX Environment.
defn walk-port (f:(JITXObject -> False), obj:JITXObject) -> False :
match(port-type(obj)):
(s:SinglePin): f(obj)
(b:Bundle|PortArray):
for p in pins(obj) do:
walk-port(f, p)
pcb-module top-level:
port bus : i2c[3]
var cnt = 0
for single in bus walk-port :
cnt = cnt + 1
println("Total Pins: %_" % [cnt])
; Prints
; Count: 1
; Count: 2
; Count: 3
; Count: 4
; Count: 5
; Count: 6
The i2c
bundle has 2 pins and there are 3 i2c
ports in the array which results in 6 total pins.
In this example, we construct a Sequence Operator
that will allow us to walk the pins of a port. The structure of a Sequence Operator
is:
defn seq-op (func, seq-obj) :
...
Where the seq-obj
is a Sequence
of objects. The for
statement will iterate over the objects in the seq-obj
sequence and then call func
on each of the objects in the sequence. The value returned by this function can optionally be captured and returned.
In our example, walk-port
is a Sequence Operator
that doesn't return any result. Notice how walk-port
has replaced the do
operator that we normally see in a for
loop statement.
So where does the func
function come from then? The for
statement constructs a function from the body
of the for
statement. In this example, the function effectively becomes:
defn func (x:JITXObject) -> False :
cnt = cnt + 1
Notice that this function is a closure. It is leveraging the top-level
context to access the cnt
variable defined before the for
statement.
A similar construction in Python might look like:
cnt = 0
def func(signal):
global cnt
cnt = cnt + 1
for x in walk-port(bus):
func(x)
Where walk-port
would need to be implemented as a generator
in python that constructs a sequence.
Check Statements
Checks are how we check our designs for correctness. We can write arbitrary code to scan through our designs, inspect the data and make sure the circuit will work as designed. The checking functions contain code that generates a well formatted report, that prompts us to enter more data or points out errors in our design.
ocdb/utils/checks
has a large set of common checks, and code to apply those automatically checks to a design.
We can also define checks that are specific to a circuit or component. The goal is to create circuit generators that check themselves for correctness, making our design work more reusable.
Example check for AEC ratings
pcb-check aec-q200 (component:JITXObject):
#CHECK(
name = "Automotive rating"
description = "Check that a passive component is AEC Q200 rated"
condition = has-property?(component.aec-rating),
category = "Component Data"
subcheck-description = "Check that %_ has a defined aec-rating" % [ref(component)],
pass-message = "%_ has a property for aec-rating of %_" % [ref(component) property(component.aec-rating)],
info-message = "%_ does not have an aec-rating property attached" % [ref(component)],
locators = [instance-definition(component)]
)
#CHECK(
name = "Automotive rating"
description = "Check that a passive component is AEC Q200 rated"
condition = property(component.aec-rating) == "Q200",
category = "Component Checks"
subcheck-description = "Check that %_ is AEC Q200 rated." % [ref(component)],
pass-message = "%_ is AEC Q200 rated" % [ref(component)],
fail-message = "%_ is not AEC Q200 rated. Instead has rating %_." % [ref(component) property(component.aec-rating)],
locators = [instance-definition(component)]
)
pcb-module checked-design :
inst r : chip-resistor(1.0)
check aec-q200(r)
Statements
Here is the list of all of the statements you can use in a pcb-check
:
Statement | Description |
---|---|
#CHECK | Registers a check to add to report. |
#CHECK
We use #CHECK
statements to evaluate conditions in our designs, and then show a Pass
Add Info
or Fail
state.
Syntax
pcb-check aec-q200 (component:JITXObject):
#CHECK(
name = "Automotive rating"
description = "Check that a passive component is AEC Q200 rated"
condition = has-property?(component.aec-rating),
category = "Component Data"
subcheck-description = "Check that %_ has a defined aec-rating" % [ref(component)],
pass-message = "%_ has a property for aec-rating of %_" % [ref(component) property(component.aec-rating)],
info-message = "%_ does not have an aec-rating property attached" % [ref(component)],
locators = [instance-definition(component)]
)
#CHECK(
name = "Automotive rating"
description = "Check that a passive component is AEC Q200 rated"
condition = property(component.aec-rating) == "Q200",
category = "Component Checks"
subcheck-description = "Check that %_ is AEC Q200 rated." % [ref(component)],
pass-message = "%_ is AEC Q200 rated" % [ref(component)],
fail-message = "%_ is not AEC Q200 rated. Instead has rating %_." % [ref(component) property(component.aec-rating)],
locators = [instance-definition(component)]
)
Description
The above #CHECK
statements generate this report, when run on a component with instance nae r[0]
.
name
The name of the check
description
The top-level description of the check in the report
condition
A boolean expression that evaluates to true
or false
. This condition defines if the check passes, or fails.
category
The top-level category to organize this check in the report.
subcheck-description
A detailed description of the result of this specific #CHECK
pass-message
What should be printed in the report if condition
resolves to true
fail-message
What should be printed in the report if condition
resolves to false
and the #CHECK
describes a design error. A #CHECK
can have a fail-message
or a pass-message
but not both.
info-message
What should be printed in the report if condition
resolves to false
and the #CHECK
describes missing data. A #CHECK
can have a fail-message
or a pass-message
but not both.
locators
A list of code locators that help the reader of the report what specific aspect of the design needs attention when the #CHECK
fails.
Components
A pcb-component
statement defines a model for a single part that can be placed on a board. There are many examples of component definitions to be found in the open-components-database. Components have ports, schematic symbols, a landpattern, and associated metadata.
The primary functions of the pcb-component
statement are:
- Provide the sourcing information to include this component in the BOM
- Provide the mapping between schematic symbol pins and land pattern pads.
- Provide the electrical interface to a component and make abstractions where necessary.
- Provide engineering data for signal integrity and other analysis.
Signature
pcb-component comp-name (arg1:Type1, ...) :
name = <String>
description = <String|False>
manufacturer = <String|False>
mpn = <String|False>
datasheet = <String|False>
reference-prefix = <String>
<PORT-1>
...
<PIN-PROP-TABLE-1>
...
<SYMBOL>
<LANDPATTERN>
; Simulation Support
emodel = <MODEL>
; SI Pin Model Definitions
pin-model(<PORT-1>) = PinModel(...)
pin-model(<PORT-1>, <PORT-2>) = PinModel(...)
; Property Definitions
property(self.<PROP-NAME-1>) = <JITXValue>
...
property(self.<PORT-1>.<PROP-NAME>) = <JITXValue>
...
<SUPPORTS-1>
...
no-connect(<PORT-1>)
<EVAL-WHEN-1>
...
The expression name comp-name
uniquely identifies this component definition
in the current context.
The argument list (arg1:Type1, ...)
is optional and provides a means of
constructing parameterized component definitions.
Required Parameters
<SYMBOL>
- The symbol statement defines the schematic symbol representation for this component. There are two primary forms:- Symbol Mapping Form - Explicit Mapping of component ports to symbol pins
- Symbol Unit Form - Allows mapping multiple symbol units to the ports of a component.
- The
assign-symbol
utility is a tool for automating this mapping with the help of the pin property table. - Only one
symbol
statement is allowed per component.
<LANDPATTERN>
- The landpattern statement defines the land pattern (or footprint) for this component and the mapping from component ports to land pattern pads.- The
assign-landpattern
utility is a tool for automating this mapping with the help of the pin property table. - Only one
landpattern
statement is allowed per component.
- The
Optional Parameters
name
- This name is used in the Board UI as a more user friendly name. If this string is not provided then thecomp-name
expression is used as the component's name.description
- This string is defining more meta-data for the component.manufacturer
- This string defines the manufacturer for this component.mpn
- This string defines the manufacturer's part number for this component.datasheet
- This string defines the URL to the datasheet for this component.reference-prefix
- This string defines the reference designator prefix, for exampleC
for capacitors,R
for resistors,Q
for transistors, etc. It not provided, this defaults toU
.<PORT-1>
- A component typically has zero or more port statements.<PIN-PROP-TABLE-1>
- A component may have 1 or more pin property tables that provide a convenient way to specify the symbol to landpattern mapping.emodel
- The emodel statement allows the user to associate a simulation model with this component. Only one statement of this type is allowed per component definition.pin-model
- The pin-model statement is a means of associating a Signal Integrity model with a particular component's ports.property
- The property statement allows the user the opportunity to associate data with the component itself or with individual ports of the component.<SUPPORTS-1>
- The supports statement allows the user to define pin assignment problems.no-connect
- The no-connect statement allows the user to mark a port on a component as not connected to any net and reserving that state as not an error.<EVAL-WHEN-1>
- The eval-when statement provides a convenient method to generate checks on a component later in the design run after the circuit's structure has been built.
Usage
Here is an example definition for an Epson FC-135 crystal oscillator:
pcb-component epson-fc-135 :
name = "32.768kHz Crystal"
description = "CRYSTAL 32.7680KHZ 7PF SMD"
manufacturer = "Epson"
mpn = "FC-135 32.768KA-AG0"
reference-prefix = "Y"
; Port Declarations
port p : pin[[1 2]]
; Symbol to Land Pattern Mapping
val sym = crystal-sym(0)
symbol = sym(p[1] => sym.p[1], p[2] => sym.p[2])
landpattern = xtal-2-3215(p[1] => xtal-2-3215.p[1], p[2] => xtal-2-3215.p[2])
property(self.load-capacitance) = 7.0e-12
property(self.shunt-capacitance) = 1.0e-12
property(self.motional-capacitance) = 3.4e-15
property(self.ESR) = 70.0e3
property(self.frequency) = 32.768e3
property(self.frequency-tolerance) = 20.0e-6
property(self.max-drive-level) = 0.5e-6
It is common to place the manufacturer
, mpn
, datasheet
, and similar labels near the top of a pcb-component
definition.
The port declarations define the electrical interface to this component.
The symbol and landpattern statements define the mapping between the symbol, the landpattern, and the electrical interface of this component.
The property statements provide meta-data and characteristics for this device or for any of its ports.
Port Declaration
There are three main ways to declare the ports of a pcb-component
.
- Explicit Port Declarations
- Pin Property Table
- Combination of Explicit and Pin Property Table
The epson-fc-135
definition above is an example of "Explicit Port Declarations". With explicit port declarations, we must use the explicit mapping forms of the symbol and landpattern statements.
The Pin Properties Table is a convenience mechanism for mapping the component ports to schematic symbol pins and land-pattern pads. It also simplifies the process of constructing ports.
pcb-component NPN :
pin-properties:
[pin:Ref | pads:Int ...]
[b | 1 ]
[c | 2 ]
[e | 3 ]
assign-symbol(bjt-sym())
assign-landpattern(SOT95P280X100-3N)
Notice that in this example - there are no port
statements to explicitly declare the ports b
, c
, or e
. The pin-properties
table constructs these ports as SinglePin
ports if they are not found at run time.
Combining Explicit & Pin Properties Table
Sometimes is very useful to combine these two approaches. This is especially true when we want the interface to a component to be bundle defined. For example:
pcb-component accelerometer:
port bus : i2c
pin-properties:
[pin:Ref | pads:Int ... | side:Dir ]
[bus.sda | 1 | Left ]
[bus.scl | 2 | Left ]
[VDD | 3 | Right ]
[GND | 4 | Left ]
[ADDR | 5 | Right ]
make-box-symbol()
assign-landpattern(SOT95P280X145-5N)
This example is a bit contrived - but the idea here is that we can forward declare ports that are not of type SinglePin
. In this example, bus
is a Bundle
type port. The properties table can then refer to individual SinglePin
ports of the bus
port. In this case the i2c
bundle only has the sda
and scl
ports.
Supports & Requires
The pcb-component
statement can contain supports
statements. These statements provide a means of constructing pin assignment problems.
pcb-component MCU:
pin-properties:
[pin:Ref | pads:Int ... ]
for i in 1 through 16 do:
[ PA[i] | i ]
supports i2c:
i2c.sda => PA[2]
i2c.scl => PA[3]
While supports
statements can be used at will in a pcb-component
context, the require
statement is more restricted. Placing a restrict
statement in the body of a pcb-component
statement will result in an error:
pcb-component MCU:
pin-properties:
[pin:Ref | pads:Int ... ]
for i in 1 through 16 do:
[ PA[i] | i ]
supports i2c:
i2c.sda => PA[2]
i2c.scl => PA[3]
require bus:i2c from self
The last line of this example will elicit the following error:
Errors occurred during parsing:
... Syntax Error: Expression expected here.
The require
statement can be used from within a supports
statement:
pcb-component MCU:
pin-properties:
[pin:Ref | pads:Int ... ]
for i in 1 through 16 do:
[ PA[i] | i ]
for i in i through 16 do:
supports gpio:
gpio.gpio => PA[i]
supports i2c:
require p1:gpio
require p2:gpio
i2c.sda => p1
i2c.scl => p2
Notice that this creates a cascade of supports
and require
statements. We call this a Nested
supports or require statement. We first create a supports
statement for each of the GPIO pins in port PA
. Then in the supports i2c:
statement, we require
two of these GPIO pins and assign them to the pending ports for this supports
statement (ie i2c.sda
and i2c.scl
).
This syntax will not result in a parsing error.
Mapping to the >VALUE
template
In a pcb-component
definition, there are two ways to set the >VALUE
template parameter:
- Use the mpn statement - Directly copies the string value of MPN to the
>VALUE
template locations. - Use the emodel statement - Formats the value (resistors, capacitance, etc) with proper unit prefix (ie,
4.7e3
maps to4.7k
) and generates a string representation.
The emodel
statement takes higher precedence than the mpn
. So if you add both statements,
then the emodel
value will override the mpn
.
There is also a value-label statement within the pcb-module context that can override the value from outside the pcb-component
definition on a per instance basis.
Statements
Here is the list of all of the statements you can use in a pcb-component
:
Statement | Description |
---|---|
datasheet | URL reference to Datasheet for component |
description | Description for the component |
emodel | EModel for the component |
eval-when | Conditional evaluation of code. |
landpattern | Physical land-pattern/footprint for the component. Also mapped to component ports. |
manufacturer | Manufacturer of the component |
mpn | Manufacturer part number of the component |
name | Name of the component |
no-connect | Set a port as "Not Connected" |
pin-properties | An easy way to map component ports to pins on a landpattern. |
ports | Ports usable when this component is instantiated in a module |
property | Properties of the component or its ports. |
reference-prefix | Start of the reference designator (default is "U"). |
require | Construct Abstract Ports inside supports statements |
supports | Supported peripherals for automated pin solving. |
symbol | Schematic symbol for the component. Mapped to the defined ports. |
Datasheet
The datasheet
statement defines the link to the datasheet of a component.
This statement is valid in the pcb-component context.
Signature
datasheet = <String|False>
This statement is optional. The default value will be false
in case no datasheet
statement is found in a component definition.
Any string is a valid datasheet
value. By convention this is typically a URL.
Each pcb-component
may have exactly one datasheet
statement. If more than one statement is encountered in a definition, then a DuplicateCStmtError
exception will be raised.
Example
pcb-component oscillator:
datasheet = "https://www.analog.com/media/en/technical-documentation/data-sheets/6906fc.pdf"
Here the datasheet
links to Analog Devices domain to the LTC6906 component at URL https://www.analog.com/media/en/technical-documentation/data-sheets/6906fc.pdf
.
The datasheet
URL is used to populate the Design Explorer component properties:
Description
The description
statement is the optional descriptive field of a JITX object. Use it to store a description of the object for human designers reading JITX, and to make the object easier to find via text search. This description also shows up in the UI, such as the design explorer, to provide more insight into particular components and modules in the design.
Signature
description = <String|False>
This statement is optional. The default value will be false
in case no description
statement is found in a definition.
Any string is a valid description
value.
Each JITX definition may have exactly one description
statement. If more than one statement is encountered in a definition, then a DuplicateCStmtError
exception will be raised.
Examples
; We can use string literals to describe a particular component.
pcb-component analog-devices-ADM7150 :
description = "800 mA Ultralow Noise, High PSRR, RF Linear Regulator"
; We can use string formatting to construct descriptions based on variables
; or arguments to a JITX Definition
pcb-module band-pass-filter (high-cut:Double, low-cut:Double) :
description = to-string(
"Band-pass Filter - Highpass = %_ Hz and Lowpass = %_ Hz." % [high-cut, low-cut]
)
EModel
The emodel
statement associates an electrical model with a component type.
This statement is only valid from within a pcb-component
definition.
Signature
emodel = <EModel>
The emodel
statement is optional. If the user does not provide an emodel
statement, then no model is associated with this component.
The EModel
is a type defined in the jitx
package. There are currently 3 JITX-defined EModel
types
defined:
Resistor
Capacitor
Inductor
Currently, these are the only assignable models to pcb-component
definitions. We plan to add generic SPICE model statements in the future.
Usage
Here is a typical application of the emodel
statement:
pcb-component my-resistor:
pin-properties:
[pin:Ref | pads:Int ... ]
[p[1], | 1 ]
[p[2], | 2 ]
emodel = Resistor(
100.0, ; Value - 100 ohm
0.05, ; Precision value between 0 and 1 - (eg 5%)
0.25 ; Power rating in Watts - 0.25 W
)
Each EModel
type has different parameters for the different types of components. See below for more details.
EModel Types
Below is a listing of currently available models and their parameters.
Resistor
defstruct Resistor <: EModel :
resistance: Double
tolerance: Double
max-power: Double
resistance
- Resistance value of this resistor in ohmstolerance
- Precision of this resistor as a value from 0.0 - 1.0.- Example:
0.05
=> 5%0.01
=> 1%0.001
=> 0.1%
- Example:
max-power
- Max Power Rating of the component in watts.
Signatures:
There are multiple forms of the constructor for the Resistor
Emodel:
defn Resistor (resistance:Double) -> EModel:
...
defn Resistor (resistance:Double, tolerance:Double) -> EModel:
...
defn Resistor (resistance:Double, tolerance:Double, max-power:Double) -> EModel:
...
Optional values tolerance
and max-power
are just not set if not provided.
Capacitor
defstruct Capacitor <: EModel :
capacitance: Double
tolerance: Double
max-voltage: Double
polarized?: True|False
low-esr?: True|False
temperature-coefficient: String
dielectric: String
capacitance
- Capacitance value in Faradstolerance
- Precision of this capacitor as a value from 0.0 - 1.0.- Example:
0.2
=> 20%
- Example:
max-voltage
- Max working voltage in Voltspolarized?
- Boolean indicating whether this is a polarized capacitor or not.low-esr?
- Boolean flag indicating if the ESR of the capacitor is below a threshold. This is currently not well defined and not recommended for usage.temperature-coefficient
- Typically a string value likeX7R
orC0G
dielectric
- Typically a string value likeCeramic
,Tantalum
, etc
Signatures for the constructor are similar to the Resistor
EModel type.
Inductor
defstruct Inductor <: EModel :
inductance: Double
tolerance: Double
max-current: Double
inductance
- Resistance value of this resistor in ohmstolerance
- Precision of this inductor as a value from 0.0 - 1.0.- Example:
0.15
=> 15%
- Example:
max-current
- Max current rating of the component in Amps. This is typically not the saturation current.
Signatures for the constructor are similar to the Resistor
EModel type.
Introspection
The emodel?
query command returns the electrical model of an instance or a component definition.
defn emodel? (obj:Instance) -> EModel|False:
...
The function returns false if:
- There is no electrical model
- The argument is not a single component instance.
Example:
val r1 = res-strap(power-5v, signal, 10.0e3)
val m = emodel?(r1)
println("Model: %_" % [m])
; Prints:
; Model: Resistor(10000.0, 0.05, 0.063)
You may see a different printout depending on what resistor gets selected in the database query.
Mapping to the >VALUE
template
The EModel
value of a resistor, capacitor, or inductor is used to populate the >VALUE
string template in pcb-symbol
and pcb-landpattern
definitions.
Example:
pcb-symbol symb:
...
draw("foreground") = Text(">VALUE", 1.0, C, loc(10.0, 12.0))
pcb-landpattern lp:
...
layer(Silkscreen("values", Top)) = Text(">VALUE", 1.0, C, loc(1.0, 2.0))
pcb-component my-R:
assign-symbol(symb)
assign-landpattern(lp)
emodel = Resistor(100.0)
In this example, the resistance
value is 100.0
. In the schematic and the landpattern, the >VALUE
string will render as 100R
.
The eval-when
Statement
When constructing a component or module, there are often times checks we want to write that are dependent on the application. We need to know more about the other circuits we are connected to before we can write the check. To delay the evaluation of these checks until we have the information we need - we use the eval-when
statement.
The eval-when
statement is valid in the following contexts:
The
eval-when
statements work in tandem with the run-evals command. Therun-evals
function traverses the passed module and orchestrates the running of variouseval-when
blocks. This means foreval-when
statements in your modules/components to run - you must callrun-evals
at some point after you have completed your design declarations.
Signature
eval-when <CONDITION> :
<STATEMENT-1>
...
- The
<CONDITION>
is a predicate (ie, something that evaluates toTrue|False
). This condition indicates what data needs to be present in order for thiseval-when
to run. - The
<STATEMENT-1>
is a list of statements that are valid for the current context. This list of statements is typically called thebody
of theeval-when
statement. This can be general stanza code or any validpcb-component
orpcb-module
statements, depending on their respective contexts.
Usage
The eval-when
statement is a powerful method of customizing modules for the context in which those modules are instantiated. This method allows us to keep the logic that applies to a particular module or component co-located with the definition of that module or component.
pcb-component LDO :
pin-properties:
[pin:Ref | pads:Int ... ]
[VIN | 1 ]
[GND | 2 ]
[VOUT | 3 ]
...
property(self.VOUT.voltage) = 3.3
property(self.MAX_VIN) = 12.0
property(self.MAX_DROP_OUT) = 0.2
eval-when has-property?(self.VIN.voltage) :
val max-rating = property(self.MAX_VIN)
if self.VIN.voltage > max-rating:
fatal(
"Invalid Input Voltage - %_ exceeds max rating of %_" %
[self.VIN.voltage, max-rating]
)
val min-rating = property(self.VOUT.voltage) + property(self.MAX_DROP_OUT)
if self.VIN.voltage < min-rating :
fatal(
"Invalid Input Voltage - %_ is less than min rating of %_" %
[self.VIN.voltage, min-rating]
)
In this case, we're defining a simple, fixed-voltage LDO regulator. We want to check that the voltage applied to the VIN
port is within the expected and allowed range for this component.
For these checks to work - we need to know what voltage is being applied to the VIN
port and we don't necessarily know that when the component is instantiated. The eval-when
is waiting for the application of a voltage
property on the VIN
port. Some other entity is going to apply this at the application layer.
Once the voltage
property exists on VIN
, the statements in the body will execute. In this case we do some minimal checks on the accepted voltage range of VIN
.
With Great Power - Comes Great Responsibility
You might notice that this adds some conditional logic to what would otherwise be a purely declarative component or module. This is a concept that doesn't really exist in other legacy CAD tools - ie, you don't typically have to worry about the components mutating.
The safest operations to use inside an eval-when
clause are operations that don't modify the physical nature of the PCB:
- Checks - These are typically non-mutating and only read properties or structures.
- BOM Variations - Changing the BOM or any variants is usually OK.
- Adding
no-connect()
statements - Adding
property()
statements
Operations that are more difficult to use consistently in an eval-when
body include:
- Adding
inst
andnet
statements - Adding concrete ports with the
port
statement. - Adding new abstract ports with the
require
statement.
There are certainly cases where you might want to use these statements in an eval-when
body. These statements are supported and will execute as part of the design run. We suggest proceeding with care and purpose when using these statements.
Land Pattern
The landpattern
statement defines the mapping between the component's ports and the pads of the physical land-pattern.
A pcb-component
can have only one associated land-pattern.
Signature
; Explicit Mapping Form
landpattern = <LP>(
<PORT-1> => <LP>.<PAD-1>,
<PORT-2> => <LP>.<PAD-2>,
...
)
; Pin Properties Table Form
assign-landpattern(<LP>)
landpattern =
statement assigns a landpattern and provides the port to pad mapping explicitly.<LP>
- This is a pcb-landpattern definition that will be assigned to the component.<PORT-1>
- Refs to individual,SinglePin
ports of the component<PAD-1>
- Refs to individual pads of the<LP>
land pattern. Note the use ofdot
notation to specify the pad.- The
=>
operator is a mapping operator.
assign-landpattern
- This is a utility function that leverages the pin-properties table to automate the construction of the port to pad mapping.- The
<LP>
is a pcb-landpattern definition
- The
Usage
The landpattern
statement is primarily used in two forms:
- Explicit Mapping Form
- Pin Properties Table Form
Explicit Mapping Form
public pcb-component kelvin-resistor :
manufacturer = "Ohmite"
mpn = "FC4L16R010FER"
port p : pin[[1, 2]]
port sense : diff-pair
val lp = kelvin-lp
landpattern = lp(
self.p[1] => lp.p[1]
self.p[2] => lp.p[2]
self.sense.P => lp.p[3]
self.sense.N => lp.p[4]
)
pcb-landpattern kelvin-lp :
pad p[1] : smd-pad(0.45, 1.2) at loc(0.3625, 0.0)
pad p[2] : smd-pad(0.45, 1.2) at loc(-0.3625, 0.0)
pad p[3] : smd-pad(0.45, 0.4) at loc(0.3625, 1.1)
pad p[4] : smd-pad(0.45, 0.4) at loc(-0.3625, 1.1)
; More Geometry Here
...
When the port names for the component are different from the pad names in the land pattern, we typically need to use the explicit mapping form of the landpattern
statement.
This mapping expects the following conditions to be met:
- Each mapping must be from a
SinglePin
port to a singlePad
on the landpattern. Bundles and PortArrays cannot be mapped with this syntax. - Each component port must have an explicit map. Forgetting a port in the mapping will result in an error.
- Each component port does not necessarily have to be unique. You could have multiple mappings of
GND => p[1]
,GND => p[2]
, etc. More on this later. - Each land-pattern pad must be used in only one mapping. If you were to make two mappings with different component ports that both map to
lp.p[1]
, this will elicit an error.
Pin Properties Table Form
Another way to construct the same component and landpattern mapping would be to use the pin-properties table.
public pcb-component kelvin-resistor :
manufacturer = "Ohmite"
mpn = "FC4L16R010FER"
pin-properties:
[pin:Ref | pads:Ref ...]
for i in 1 through 4 do:
[p[i] | p[i] ]
assign-landpattern(kelvin-lp)
supports diff-pair:
diff-pair.P => self.p[3]
diff-pair.N => self.p[4]
Instead of making an explicit port sense:diff-pair
, this version opts to use a supports statement as a means of exposing the differential sense lines of the kelvin resistor. Notice that this version makes no explicit port
declarations at all. They are all implicitly defined via the pin-properties
table. Upon evaluation, the pin-properties
table will create any unknown component ports it finds in the pin
column.
In this particular case, the diff-pair
supports statement acts more like an interface definition as opposed to a pin-assignment problem. There is only one option for the diff-pair
so the solution is trivial. This has the nice benefit of abstracting away what type of component is used to provide the sense interface. For example, it would be easy to swap the kelvin-resistor
component with a pcb-module
with the same interface but containing a different sensing technique.
One Port to Many Pads
In many electronic components there are multiple pads that map to one functional connection. For example, the ground pin may have 1 or many pads on a component. To support this case, the landpattern
statement allows a port ref to be present multiple times. For example in an SMA connector, it is common to have 4 through-hole pads for ground around a center signal pin:
pcb-component SMA :
port launch : pin
port gnd : pin
val lp = SMA-lp
landpattern = lp(
launch => lp.p[1],
for i in 2 through 5 do:
gnd => lp.p[i]
)
val symb = ocdb/utils/symbols/coax-sym
symbol = symb(
launch => symb.sig,
gnd => symb.gnd
)
pcb-landpattern SMA-lp:
pad p[1] : pth-pad(0.75, 1.3) at loc(0.0, 0.0)
pad p[2] : pth-pad(0.8, 1.4) at loc(2.55, 2.55)
pad p[3] : pth-pad(0.8, 1.4) at loc(2.55, -2.55)
pad p[4] : pth-pad(0.8, 1.4) at loc(-2.55, 2.55)
pad p[5] : pth-pad(0.8, 1.4) at loc(-2.55, -2.55)
; More Geometry Here
In this example, notice that we use a for-loop
to connect the ground pin to the 4 ground through-hole pads of the SMA connector. This has a side effect in the schematic:
Notice that the ground pin on the coax-symb
lists its pads as p[2-5]
. This is indicating that the ground symbol maps to pins p[2]
, p[3]
, p[4]
, and p[5]
.
This syntax is very concise for this simple example. Be warned that this syntax doesn't scale as well when considering, for example, a large FPGA with 10s or 100s of ground pins:
In this case, you will likely be better off constructing a PortArray
for the GND
port and using individual pad mappings:
pcb-component FPGA:
pin-properties:
[pin:Ref | pads:Ref ...]
[GND[0] | A[1] ]
[GND[1] | A[5] ]
[GND[2] | A[9] ]
...
This results in a more readable schematic symbol even if it is a bit larger:
Land Pattern Pads are Internal to Components
The land-pattern and its associated pads are not intended to be exposed outside the component. The components ports
are the external interface to a component. Any attempt to net
to a pad of a component will result in an error.
Notice that in the above examples, the component port and the landpattern pads use the same ref names. So while this may not be unambiguous - know that the port
is what we connect to when constructing the circuit and net list.
Manufacturer
The manufacturer
statement defines the manufacturer of a component. It is used to create the bill of materials and to check the sourceability of a pcb-component
.
This statement is valid in the pcb-component context.
Signature
manufacturer = <String|False>
This statement is optional. The default value will be false
in case no manufacturer
statement is found in a component definition.
Any string is a valid manufacturer
value.
Each pcb-component
may have exactly one manufacturer
statement. If more than one statement is encountered in a definition, then a DuplicateCStmtError
exception will be raised.
Example
pcb-component TPD3S014DBVR:
manufacturer = "Texas Instruments"
manufacturer = "Texas Instruments"
The manufacturer of this component is "Texas Instruments"
MPN
The mpn
statement defines the "Manufacturer Part Number" of a component. It is used to create the bill of materials and to check the sourceability of a `pcb-component``.
This statement is valid in the pcb-component context.
Signature
mpn = <String>
This statement is optional. The default value will be false
in case no mpn
statement is found in a component definition.
Any string is a valid mpn
.
Each pcb-component
may have exactly one mpn
statement. If more than one statement is encountered in a definition, then a DuplicateCStmtError
exception will be raised.
Examples
pcb-component usb-iface:
mpn = "TPD3S014DBVR"
pcb-component dip-switch (n-sw:Int = 8)
mpn = to-string("219-%_MSTR" % [n-sw])
In the usb-iface
example, mpn = "TPD3S014DBVR"
The part number of this component is TPD3S014DBVR
Parametrically constructed components are common pattern. In the dip-switch
example, we have an argument n-sw
to the component definition. We use this argument value to construct the mpn
using string formatting. For the default argument of 8
, this results in mpn = "219-8MSTR"
.
Name
The name
statement is the optional name field of a JITX object. Use it to store a descriptive name as a String
. This name will often be used in the UI in place of the object's expression name for better readability.
Signature
name = <String>
This statement is optional. The default value will be the definition's symbol name in case no name
statement is found in a definition.
Any string is a valid name
value.
Usage
Literal String Names
pcb-component component :
name = "ADM7150"
pcb-module band-pass-filter :
name = "Band-pass filter"
The examples name = "ADM7150"
and name = "Band-pass filter"
use a String
literal for the name.
Formatted Strings
pcb-pad smd-pad (anchor:Anchor, w:Double, h:Double) :
name = to-string("%_x%_ %_ SMD Pad" % [w,h,anchor])
pcb-landpattern test-lp:
pad p[1] : smd-pad(C, 0.6, 0.7) at loc(x0,y0)
We can also construct strings using formatting routes and parameter arguments for a particular definition. In this example, the constructed smd-pad
name property would be 0.6x0.7 C SMD Pad
No Connect Statement
The no-connect()
statement is a tool for marking a port on a component or module as "Not Connected". This is an indication to the JITX runtime that this port can be left floating without any ill-effect.
This statement can be used from the following contexts:
Signature
no-connect(<PORT>)
<PORT>
- The argument to this statement is expected to be aSinglePin
port from apcb-component
definition or instance.
Usage
The no-connect()
statement is typically used to mark individual pins as intentionally not connected:
public pcb-component component :
name = "XDCR_LIS3DHTR"
manufacturer = "STMicroelectronics"
mpn = "LIS3DHTR"
pin-properties :
[pin:Ref | pads:Int ... | side:Dir]
[CS | 8 | Left]
[GND | 5, 12 | Down]
...
[nc[0] | 2 | Down]
[nc[1] | 3 | Down]
[VDD-IO | 1 | Up]
make-box-symbol()
assign-landpattern(xdcr-lis3dhtr)
for i in indices(self.nc) do:
no-connect(self.nc[i])
Here the LIS3DHTR
has two "NC" pins, pin 2 and 3. This component defines these pins in a PortArray
of length 2. The for-loop at the end uses the indices command to loop over all the NC pins.
Notice - that we did not pass self.nc
, the PortArray
, directly to the no-connect
statement. This would elicit an error.
When we view this component in the schematic, we see:
Notice the X
over the two NC pins. This is the "No Connect" representation in the schematic.
Usage from pcb-module
When using this statement from a module, we must use the no-connect()
statement on one of the ports of the instantiated components in the module. It is an error to apply the no-connect()
statement to one of a module's ports.
public pcb-module module :
public inst acc : ocdb/components/st-microelectronics/LIS3DH/component
...
for i in indices(acc.nc) do:
no-connect(acc.nc[i])
Note that duplicate no-connect()
statements on a component's port will not throw an error.
If you attempt to use the no-connect
statement on a module's port:
pcb-module bad-module:
port unused : pin
no-connect(self.unused)
This will throw an exception:
Uncaught Exception: Tried to get type with an invalid definition.
Testing for "No Connect" Status
The no-connect? command can be used to interrogate a component and determine the "No Connect" status of its pins.
Pin Properties
The pin-properties
statement defines a table for quickly and easily constructing the ports for a component. In addition to defining the ports, the pin-properties
table can also easily add property
statements to each pin in the table. This statement is effectively an automation tool for more easily generating lower level JITX statements.
The pin-properties
statement is valid in the pcb-component
and pcb-module
contexts. Each component or module may contain one or more pin-properties
tables.
While you can use pin-properties
in the pcb-module
context, it is most typically found
in the pcb-component
context.
Signature
pin-properties:
<HEADER>
<ROW-1>
<ROW-2>
...
<HEADER>
- Defines the schema for each following rows of the pin properties table.<ROW-*>
- Each row describes aSinglePin
port of this component.
<HEADER> Signature
[ <NAME-1>:<TYPE-1> | <NAME-2>:<TYPE-2> | ... ]
The <HEADER>
consists of a fixed-length set of <NAME>:<TYPE>
pairs. The <NAME>
value defines the name of the property for this table. Commonly used property names include:
pin
- This identifies whichSinglePin
port of the component that the following properties apply to. This is typically aRef
type.pads
- This identifies which pads on the land pattern map to a particular. This is typically used with eitherInt
orRef
type and is often combined with the...
operator. See more information below.bank
- This identifier is used withInt
orRef
type to identify the banks of a multi-part symbol.side
- This identifier is commonly used with the "Box Symbol" generators. This property is used with theDir
type to indicate which side of the box a pin will be placed on.
Beyond these properties, any identifier and type can be used to add custom properties to ports.
The ...
Operator
The ellipsis points operator ...
is used in the header to indicate that this column may contain multiple values. This is typically used for the pads
identifier to map multiple land pattern pads to a specified port.
<ROW-*> Signature
[ <VALUE1> | <VALUE2> | ... ]
Each row contains the properties to assign to a particular component SinglePin
port. The row's pin
property is typically the first column and identifies which port the following properties will apply to. The pin
property typically also matches the schematic symbol's pin refs but that isn't a strict requirement. This is only convenient when using the assign-symbol
or assign-symbols
commands which handle mappings automatically if the names are consistent.
The referenced pin must be a SinglePin
- it can't be an array or a bundle port. This is important because we need to be able to apply properties like the pads
to a particular single pin.
Each row must contain the same number of columns as the header. Otherwise - an exception will be thrown.
Example Pin Properties Table
pcb-component EEPROM-24AA025:
...
pin-properties :
[pin:Ref | pads:Ref ... ]
[SDA | p[3] ]
[SCL | p[1] ]
[VCC | p[6] ]
[VSS | p[2] ]
[A0 | p[5] ]
[A1 | p[4] ]
This table is an example from the Microchip, 24AA025 EEPROM chip.
In this particular example - there is a 1:1 mapping between the schematic symbol pins and the pads of the landpattern. Both the pin
and the pads
are of type Ref
. For the pin
properties, this means that each name SDA
, SCL
, etc are defined as SinglePin
ports on this component. For the pads
, we are referencing the conventional p
array that defines the pins of a landpattern. This means that p[1]
corresponds with pin 1, p[2]
corresponds with pin 2, etc:
This table could have been defined identically as:
pcb-component EEPROM-24AA025:
...
pin-properties :
[pin:Ref | pads:Int ... ]
[SDA | 3 ]
[SCL | 1 ]
[VCC | 6 ]
[VSS | 2 ]
[A0 | 5 ]
[A1 | 4 ]
Notice that in this case pads
is of type Int
and we don't use the p[*]
syntax. We reference the integer pin directly. This is a short hand for the p[*]
syntax.
For this simple example - it may not be obvious why you might use a Ref
instead of a Int
. Typically, we see Ref
used for large BGA packages where you will see K[1]
, N[5]
, AB[2]
, or similar references from a 2D grid.
N:1 Pads to Ports
There are some cases where you may want to assign multiple land pattern pads to a single port. This is very common for ground and power pins in large packages.
pcb-component FPGA:
...
pin-properties :
[pin:Ref | pads:Ref ... ]
[ VCCINT | A[2] B[10] C[3] C[4] ]
[ GND | A[3] A[7] B[2] B[8] ]
...
In this example, we construct a component with multiple pads devoted to the VCCINT
and GND
pins.
Notice how each of the pins has multiple pads associated with it. The engine attempts to shorten the pad references shown next to the pin as much as it can. In this case, it means that for each C
pad referenced in VCCINT
, we use a ,
(comma) delimited list of indices. Similarly in GND
, both the A
and B
pads have 2 indices listed.
This style of N:1
pad to pin referencing can be useful but if the value of N
grows large, this can become unwieldy. Another way to structure this would be to use unique pins for each pad (ie, strictly 1:1
mapping):
pcb-component FPGA:
...
pin-properties :
[pin:Ref | pads:Ref ... ]
[ VCCINT[0] | A[2] ]
[ VCCINT[1] | B[10] ]
[ VCCINT[2] | C[3] ]
[ VCCINT[3] | C[4] ]
[GND[0] | A[3] ]
[GND[1] | A[7] ]
[GND[2] | B[2] ]
[GND[3] | B[8] ]
...
In this variant, each of the VCCINT
and GND
pins has a 1:1
mapping with a pad. This can be useful for the case where there may be 10's or 100's of these pins. This trades more schematic pixel space for a more readable pin to pad mapping.
Individually listing each pins in a table like this is going to become a bit tedious though. Fortunately, we're not just entering data, we can write a bit of code too:
val VCCINT-PADS = [
Ref("A")[2],
Ref("B")[10],
Ref("C")[3],
Ref("C")[4]
]
pin-properties :
[pin:Ref | pads:Ref ... ]
for (rf in VCCINT-PADS, i in 0 to false) do:
[ VCCINT[i] | (rf) ]
The VCCINT-PADS
tuple in this example is a bit contrived. The more likely source of the pad information for a component would be a file like the Xilinx Pinout Package files. The important part is the use of the for
loop to construct each of the rows of the pin-properties
table.
Note the use of the ()
around the rf
value. These parenthesis are necessary to convert the value into a Ref
symbol.
Assigning Properties for the Box Symbol
The pin
and pads
properties are not the only features that we can add through the pin-properties
table.
The header of the table is customizable and allows any number of properties to be assigned to each pin
as needed.
Here is an example where we assign a side
property of type Dir
to each pin. These types of properties
are very useful for when working with the "BoxSymbol" utilities:
pcb-component mycomponent :
mpn = "DAC53001"
pin-properties :
[pin:Ref | pads:Int ... | side:Dir ]
[VDD | 15 | Right ]
[VREF | 16 | Right ]
[OUT0 | 2 | Right ]
[FB0 | 1 | Right ]
[CAP | 13 | Right ]
[SDO | 5 | Left ]
[SYNC# | 6 | Left ]
[SDI | 7 | Left ]
[SCLK | 8 | Left ]
[NC | 3, 4, 9, 10, 11, 12 | Left ]
[AGND | 14, 17 | Left ]
val box = BoxSymbol(self)
set-alignment(N, self.VDD, self.VREF)
set-alignment(S, self.AGND, self.NC, self.CAP)
set-head-margin(1.0, self.NC)
set-head-margin(1.0, self.OUT0, self.FB0)
val symb = create-symbol(box)
assign-symbol(symb)
Here is an example rendering of this component:
Notice how the pins of the symbol get assigned to one side or the other depending on
the side
property. Notice also that the order of the pins in the box symbol depends on
the ordering in the table. Some of the details regarding the formatting are left out in this
example.
Another point of interest is that the NC
and AGND
are multi-pad pins in this symbol. Notice
how for those pins, the pad identifier for the pin has multiple pad references in it.
This symbol was created with the BoxSymbol from JSL. See JSL for more information about how to set alignment, margin, and other properties.
Assigning the Bank via the Table
The bank
property is used to construct multi-part symbols. We can assign the bank association in the pin-properties
table. The bank
property is of type Int|Ref
meaning that it can either be
an Int
value or a Ref
value. When building a part, you will typically use all Int
or all Ref
for the banks
in a component.
Here is an excerpt of a component that uses the bank
property as a Ref
in the pin properties table:
public pcb-component USBMux :
manufacturer = "Texas Instruments"
mpn = "HD3SS3220RNHR"
reference-prefix = "U"
port TX-O : diff-pair
port RX-O : diff-pair
port TX : diff-pair[[1, 2]]
port RX : diff-pair[[1, 2]]
pin-properties :
[pin:Ref | pads:Ref ... | side:Dir | bank:Ref]
[VBUS_DET | p[5] | Left | control]
[ID | p[27] | Left | control]
[CC2 | p[1] | Left | control]
[CC1 | p[2] | Left | control]
[CURRENT_MODE | p[3] | Left | control]
[PORT | p[4] | Left | control]
[ENn_CC | p[29] | Left | control]
[TX-O.P | p[6] | Right | mux]
[TX-O.N | p[7] | Right | mux]
[RX-O.P | p[9] | Right | mux]
[RX-O.N | p[10] | Right | mux]
...
This results in two symbol parts for the component:
Multiple Pin Property Tables
When defining pin-properties
tables, the number of rows typically equals the number of pins in the component we are attempting to model. With a large number of pins, this amount of data can be quickly become overwhelming. Adding or removing columns from the table becomes a chore.
Because of this it is common to keep the tables focused on one particular application at a time. For example, the primary application is creating the pin to pad mapping. That is what the first (and usually only) pin-properties
table will do.
Once you have defined this mapping, you can either add columns to this table to introduce other properties, or you create an entirely new table. If we refer back to the 24AA025
example from earlier, we could add a second table in our component definition like this:
pcb-component EEPROM-24AA025:
...
pin-properties :
[pin:Ref | pads:Int ... ]
[SDA | 3 ]
[SCL | 1 ]
[VCC | 6 ]
[VSS | 2 ]
[A0 | 5 ]
[A1 | 4 ]
pin-properties:
[pin:Ref | side:Dir ]
[SDA | Left ]
[SCL | Left ]
[VCC | Right ]
[VSS | Left ]
[A0 | Right ]
[A1 | Right ]
...
This example is a trivial case, but you could imagine adding additional custom properties or other data.
The important things to consider are:
- The
pin:Ref
column is like the "Primary Key" of this table. Everypin-properties
table instance will need to reference thepin:Ref
property as the first column. - The first
pin-properties
table defines the ports of the component. Thepin:Ref
properties in subsequent tables must match with thepin:Ref
properties in the first table.- The subsequent tables can have a sub-set of the
pin:Ref
properties from the first table. - No new, unique
pin:Ref
properties can be defined in subsequent tables.
- The subsequent tables can have a sub-set of the
For example - This is OK:
pin-properties :
[pin:Ref | pads:Int ... ]
[SDA | 3 ]
[SCL | 1 ]
[VCC | 6 ]
[VSS | 2 ]
[A0 | 5 ]
[A1 | 4 ]
; OK - properties only defined on these pins.
pin-properties:
[pin:Ref | card:Cardinality ]
[SDA | Bidir ]
[SCL | Bidir ]
[A0 | Input ]
[A1 | Input ]
We don't define any new pins - we just don't include the VCC
and VSS
in the second table. This means that SDA
, SCL
, A0
, and A1
will all have a card
property but VCC
and VSS
will not.
But the following is NOT OK:
pin-properties :
[pin:Ref | pads:Int ... ]
[SDA | 3 ]
[SCL | 1 ]
[VCC | 6 ]
[VSS | 2 ]
[A0 | 5 ]
[A1 | 4 ]
; BAD - Will throw an error
pin-properties:
[pin:Ref | pads:Int ... ]
[NC | 7 ]
[NC | 8 ]
This will result in the runtime throwing an exception.
Ignored Values on Properties
Sometimes you have a table structure and you don't want to set a property on a particular pin. The -
special value is here to the rescue. This value basically means "Don't set this property".
pin-properties :
[pin:Ref | pads:Int ... ]
[SDA | 3 ]
[SCL | 1 ]
[VCC | 6 ]
[VSS | 2 ]
[A0 | 5 ]
[A1 | 4 ]
; OK - properties only defined on these pins.
pin-properties:
[pin:Ref | card:Cardinality | i2c-bus:(True|False) ]
[SDA | Bidir | true ]
[SCL | Bidir | true ]
[A0 | Input | - ]
[A1 | Input | - ]
Notes:
- Notice that in the
i2c-bus:(True|False)
property type, there is a set of()
around theTrue|False
type. This is necessary for all Union Types inpin-properties
headers. Otherwise, you will get a syntax error. - The
-
special value basically means thatA0
andA1
in this example will not have ai2c-bus
property. Thehas-property?(self.A0.i2c-bus)
function will returnfalse
.
Pin Model
The pin-model
statement is applies a delay and loss model to a port of a component. These features are used by the signal integrity routing to determine the length matching requirements for traces on the board.
This statement is valid in the pcb-component context.
Signature
; Single-Ended Form
pin-model(<PORT>) = <PinModel>
; Pass-Through Form
pin-model(<PORT-1>, <PORT-2>) = <PinModel>
<PORT>
- The arguments to thepin-model
statement areSinglePin
ports of the component.<PinModel>
- An instance of typePinModel
that defines the delay and loss characteristics of this port or ports.
The pin-model
statement comes in two forms:
- "Single-Ended Form" - In this form, this statement describes the features of a single pin of an IC, such as a driver or receiver. This includes the delay and loss from the package die bonding and other internal features of the IC.
- "Pass-Through Form" - In this form, this statement describes the delay and loss of a signal as it passes from
<PORT-1>
through the component to<PORT-2>
. This is most commonly used with components like a resistor or DC blocking capacitor.
PinModel
The PinModel
type is the base type for defining the model of a IC's pins:
public defstruct PinModel :
delay: Toleranced ; Delay in seconds
loss: Toleranced ; Loss in decibels
The parameters of this model are Toleranced types which allows the designer to specify the manufacturing uncertainty involved with the delay and loss if known.
The base model is not very sophisticated. It doesn't consider dispersion or other factors that contribute to skew in high frequency designs. In the future, we will add S-Parameter, IBIS, and other model formats for describing component port signal integrity features.
Examples
Below is a minimal example for applying a pin model to a components pins.
pcb-component MCU:
port usb : diff-pair
pin-model(usb.P) = PinModel(typ(1.3e-12), typ(0.0001))
pin-model(usb.N) = PinModel(typ(1.1e-12), typ(0.0001))
Notes:
- The
pin-model
statement must be applied to aSinglePin
port - it can't be applied to aBundle
port. Here we apply it individually to each of theP
andN
signals of the diff-pair.
Ports
port
is a JITX statement that defines the electrical connection points for a component or module.
The port statement can be used in the following contexts:
Each port
statement in any of these contexts is public
by default. This means that each port can be accessed externally by using dot
notation.
Signature
; Single Port Instance
port <NAME> : <TYPE>
; Array Port Instance
port <NAME> : <TYPE>[<ARRAY:Int|Tuple>]
<NAME>
- Symbol name for the port in this context. This name must be unique in the current context.<TYPE>
- The type of port to construct. This can be the keywordpin
for a single pin type or any declaredpcb-bundle
type.<ARRAY:Int|Tuple>
- Optional array initializer argument. This value can be:Int
-PortArray
constructed with lengthARRAY
. This array is constructed as a contiguous zero-index array.Tuple
-PortArray
constructed with an explicit set of indices. This array is not guaranteed to be contiguous.
Watch Out! - There is no space between
<TYPE>
and the[
opening bracket of the array initializer.
Syntax
There are multiple forms of the port
statement to allow for flexible construction of the interface to a particular component. The following sections outline these structures.
Basic Port Declaration
The user has two options when declaring a single pin:
; Single Pin 'a'
port a : pin
; Equivalent to
port a
The port a
structure is a shorthand version for declaring single pins.
Pin Arrays
To construct a bus of single pins, we use the array constructor:
port contiguous-bus : pin[6]
port non-contiguous-bus : pin[ [2, 5, 6] ]
println(indices(non-contiguous-bus))
; Prints:
; [2 5 6]
The contiguous-bus
port is defined as a contiguous array of pins with indices: 0, 1, 2, 3, 4, & 5.
The non-contiguous-bus
port is defined an array with explicit indices: 2, 5, & 6. The indices
function is a helper function for determining what array indices are available on this port.
Attempting to access a port index that does not exist or is negative will result in an error.
Bundle Port
A bundle port is a connection point that consolidates multiple individual signals into one entity. The structure of this port is defined by the pcb-bundle
type used in its declaration:
pcb-bundle i2c:
port sda
port scl
pcb-module mcu:
port data : i2c
In this example, we define the i2c
bundle. Notice that this bundle is constructed from same port
statements defined above.
The data
port is then constructed with the bundle name i2c
as the type.
The individual signals of the data
port are accessible using dot
notation:
pcb-module top-level :
inst U1 : mcu
println(port-type(mcu.data.sda))
; Prints:
; [SinglePin object]
Bundle Array Port
As with the single pin, we can construct port arrays of bundle types:
pcb-bundle i2c:
port sda
port scl
pcb-module mcu:
port busses : i2c[3]
In this example, we construct a contiguous array of 3 I2C bus ports. We can access the individual signals of these busses as well:
pcb-module top-level :
inst U1 : mcu
println(port-type(mcu.busses[0].scl))
; Prints:
; [SinglePin object]
Port Types
Each port has a type associated with it. That type can be accessed with the function port-type. This function returns a PortType
instance that can be one of the following types:
SinglePin
- A port instance of typepin
Bundle
- A port instance of type BundlePortArray
- An array of port instances
We would typically use the result of this function with a match statement:
pcb-module amp:
port vout : pin[4]
match(port-type(vout)):
(s:SinglePin): println("Single")
(b:Bundle): println("Bundle")
(a:PortArray): println("Array")
This structure allows a different response depending on the type of port.
The Bundle
PortType
The Bundle
port type provides the user with access to the type of bundle that was used to construct this port:
pcb-bundle i2c:
port sda
port scl
pcb-module mcu:
port bus : i2c
pcb-module top-level:
inst U1 : mcu
match(port-type(U1.bus)):
(b:Bundle):
println("Bundle Type: %_" % [name(b)])
(x): println("Other")
; Prints:
; Bundle Type: i2c
Notice that the b
object is a reference to the pcb-bundle i2c
definition. This provides a convenient way to check if a given port matches a particular bundle type.
Walking a PortArray
It is often useful to walk a PortArray
instance and perform some operation on each of the SinglePin
instances of that PortArray
. Because PortArray
instances can be constructed from either single pins or bundles, they can form arbitrarily complex trees of signals. To work with trees of this nature, recursion is the tool of choice.
Note: This is a more advanced example with recursion. Fear not brave electro-adventurer - These examples will serve you well as you become more comfortable with the JITX Environment.
defn walk-port (f:(JITXObject -> False), obj:JITXObject) -> False :
match(port-type(obj)):
(s:SinglePin): f(obj)
(b:Bundle|PortArray):
for p in pins(obj) do:
walk-port(f, p)
pcb-module top-level:
port bus : i2c[3]
var cnt = 0
for single in bus walk-port :
cnt = cnt + 1
println("Total Pins: %_" % [cnt])
; Prints
; Count: 1
; Count: 2
; Count: 3
; Count: 4
; Count: 5
; Count: 6
The i2c
bundle has 2 pins and there are 3 i2c
ports in the array which results in 6 total pins.
In this example, we construct a Sequence Operator
that will allow us to walk the pins of a port. The structure of a Sequence Operator
is:
defn seq-op (func, seq-obj) :
...
Where the seq-obj
is a Sequence
of objects. The for
statement will iterate over the objects in the seq-obj
sequence and then call func
on each of the objects in the sequence. The value returned by this function can optionally be captured and returned.
In our example, walk-port
is a Sequence Operator
that doesn't return any result. Notice how walk-port
has replaced the do
operator that we normally see in a for
loop statement.
So where does the func
function come from then? The for
statement constructs a function from the body
of the for
statement. In this example, the function effectively becomes:
defn func (x:JITXObject) -> False :
cnt = cnt + 1
Notice that this function is a closure. It is leveraging the top-level
context to access the cnt
variable defined before the for
statement.
A similar construction in Python might look like:
cnt = 0
def func(signal):
global cnt
cnt = cnt + 1
for x in walk-port(bus):
func(x)
Where walk-port
would need to be implemented as a generator
in python that constructs a sequence.
Properties
Properties are a flexible way to add data to ports, instances, and nets. We can create and query properties inside components and modules.
The property statement is valid within the following contexts:
Signature
; Get Form
val v1:JITXValue = property(dot-path)
val v2:Maybe<JITXValue> = property?(dot-path)
val v3:JITXValue = property?(dot-path, def-value)
val v4:True|False = has-property?(dot-path)
; Set Form
property(dot-path) = 3.0
The "Get" form of the property
statement allows the user to inspect a particular property of an object. If the requested property doesn't exist, then a NoPropertyWithName
exception will be thrown.
The property?
statement is a "Get" form that will return a Maybe
element or allow for a default value def-value
. This get statement form will not throw an exception if the property doesn't exist. It will either return None()
or it will return the passed def-value
.
The has-property?
statement checks for the existence of a particular property on a particular object. This statement is often used with the eval-when statement.
The "Set" form of the property
statement allows the user to create a new property or override an existing property on an object. The value assigned to a property must be of type JITXValue
.
The argument to the property
statement is a dot notation path or dot-path
. A dot-path
will typically start with the name of an instance, net, or port, and the final element of the dot path will be the identifier for the property.
For example:
pcb-component props-test:
port gnd : pin
property(self.gnd.voltage) = 0.0
In this example, we use the special instance self
to refer to this component instance (ie, an instance of props-test
). The next element on the dot-path
is referring to the gnd
port of the props-test
instance. Finally, the element voltage
is the name of the property that is being set on the gnd
port.
Assigned Types
The value assigned to a property
must be of type JITXValue
. The JITXValue
type includes most of the built-in JITX types like Double
, Int
, Pose
, Toleranced
, String
, etc. It will not necessarily include custom, user-defined types (ie, any type created with deftype
or defstruct
). If you try to set a property with a defstruct
- you will see an exception thrown that looks something like:
Cannot call function 'set-property' with given arguments: Argument 4: Passing type 'CustomUserType' to expected type 'JITXValue'.
To assign more custom data types, you will need to define a pcb-struct type. This
is similar to the defstruct
style type definitions but adds additional features that fulfill the JITXValue
interface.
Common Component Properties
Below is an example component with common property
statement patterns:
pcb-component TPS62081:
manufacturer = "Texas Instruments"
mpn = "TPS62081DSG"
datasheet = "https://www.ti.com/lit/ds/symlink/tps62082.pdf"
pin-properties:
[pin:Ref | pads:Int ...]
[EN | 1]
[GND | 2]
[MODE | 3]
[FB | 4]
[VOS | 5]
[PG | 6]
[SW | 7]
[VIN | 8]
property(self.EN.threshold) = 1.0
property(self.PG.leakage-current) = typ-max(0.01e-6, 0.1e-6)
property(self.junction-temperature) = min-max(-40.0, 125.0)
This definition defines 3 properties, 2 on ports and 1 on the instance itself.
threshold
andleakage-current
are properties extracted from the datasheet.junction-temperature
is applied to the component via theself
keyword.
Common Module Properties
In the below example, we show a pcb-module
definition using property
statements:
pcb-module switcher :
property(self.buck) = true
port VIN : pin
port VOUT : pin
port GND : pin
property(VIN.max-voltage) = 6.0
inst IC : TPS62081
property(IC.no-clean) = true
property(IC.FB.max-voltage) = 3.6
net local-gnd (GND, IC.GND)
property(local-gnd.voltage) = 0.0
In the module context, we have the opportunity to apply properties to:
- The module instance itself via the
self
identifier - The ports of the module instance - eg
VIN
,VOUT
- The component instances defined in the module - eg
IC
in this example. - Ports of the component instances in the module - eg
IC.FB
. - The nets of the module instance - eg
local-gnd
Reference Prefix
The reference-prefix
statement defines the prefix of reference designators of each component instance created in a JITX design.
This statement is valid in the pcb-component context.
Signature
reference-prefix = <String>
Any string is valid as a reference prefix.
If no reference-prefix
statement is found in a component definition, the default prefix is "U"
.
Each pcb-component
may have exactly one reference-prefix
statement. If more than one statement is encountered in a definition, then a DuplicateCStmtError
exception will be raised.
Examples
pcb-component my-component:
reference-prefix = "X"
pcb-component my-component:
reference-prefix = "ESD"
reference-prefix = "X"
Set the reference prefix of this component to be "X". The first component would get reference designator X1
. Subsequent components will get X2
, X3
, etc
reference-prefix = "ESD"
Set the reference prefix of this component to be "ESD". The first component would get reference designator ESD1
.
Require
require
statements can be used in coordination with supports statements to automate pin assignment. When we use a require
statement it creates an abstract port
. We can use this abstract port like any other port and JITX will handle mapping that abstract port to a concrete port on a component.
The require
statement is valid in the following contexts:
Signature
; Implicit `self` form
require <NAME>:<TYPE>
require <NAME>:<TYPE>[<ARRAY>]
; Explicit form
require <NAME>:<TYPE> from <INST>
require <NAME>:<TYPE>[<ARRAY>] from <INST>
<NAME>
- Name of the created abstract port in this context. This must be a unique symbol name in the current context.<TYPE>
- TheBundle
type for the requested abstract port.<ARRAY>
- Optional Array Initializer for constructing an array of abstract port.<INST>
- TheInstance
from which we are requesting abstract port.- In the
Implicit
form, this value isself
by default. - In the
Explicit
form, we must provide a ref to a specific module or component instance in the current context.
- In the
Usage
Basic Pin Assignment
pcb-bundle i2c:
port sda
port scl
pcb-module top-level:
inst mcu : stm32f405G7
require bus:i2c from mcu
inst sensor : temp-sensor
net (bus, sensor.i2c-bus)
inst R : chip-resistor(4.7e3)[2]
net (bus.sda, R[0].p[1])
net (bus.scl, R[1].p[1])
This is a typical use case for pin assignment in a microcontroller circuit. Here we are requesting one of the 3 available I2C ports on the SM32F405 and constructing an abstract port that will map to one of them depending on what other require
statements exists as well as the board conditions.
The bus
abstract port can be used like any other port on a component or module. We can use the net
statement to connect it to other ports. We can use dot
notation to connect to individual pins of the abstract port.
Abstract Port Array
pcb-bundle gpio:
port p
pcb-module top-level:
inst mcu : stm32f405G7
val num-sw = 4
require sw-inputs:gpio[ num-sw ] from mcu
inst switches : momentary-switch[ num-sw ]
for i in 0 to num-sw do:
net (sw-inputs[i].p, switches[i].p[1])
Like other inst
or net
declarations, we can construct an array of abstract ports withe []
syntax. Here we construct 4 GPIO abstract ports requested from the mcu
instance.
We can then connect these individual gpio pins to other instance ports, like the momentary-switch
instances.
Use of require
inside supports
We often want to cascade the construction of abstract ports by using require
statements inside supports
statements.
pcb-component stm32:
port PA : pin[32]
...
pcb-bundle I2C0_SDA:
port p
supports I2C0_SDA:
option:
I2C0_SDA.p => self.PA[1]
option:
I2C0_SDA.p => self.PA[7]
pcb-bundle I2C0_SCL:
port p
supports I2C0_SCL:
option:
I2C0_SCL.p => self.PA[2]
option:
I2C0_SCL.p => self.PA[6]
supports i2c:
require sda0:I2C0_SDA
require scl0:I2C0_SCL
i2c.sda => sda0
i2c.sda => scl0
pcb-module top-level:
inst mcu : stm32
require bus:i2c from mcu
In this example, we define ad-hoc pcb-bundle
definitions I2C0_SDA
and I2C0_SCL
that are only accessible within this component's context. The supports
statements for these two bundle types effectively make private pending ports
that can only be used within this context.
Finally - we make a supports i2c:
statement that constructs an externally accessible pending port for the i2c
bundle type. The bus
abstract port that we are ultimately able to connect to the rest of our system consists of:
bus.sda
=>mcu.PA[1]
ORmcu.PA[7]
bus.scl
=>mcu.PA[2]
ORmcu.PA[6]
Notice that in the supports i2c:
statement, the require
statement uses the Implicit
form (ie, the lack of a from <INST>
part of the statement). This means that this require
statement is targeting self. This statement is exactly the same as:
require sda0:I2C0_SDA from self
Check out restrict
There is also the restrict statement which provide another method of defining the constraints for the pin assignment problem. The restrict
statement operates on the abstract port
instances defined by the require
statement.
Supports
supports
statements can be used in coordination with require statements to automate pin assignment. We use supports
to describe valid ways pins can be assigned. A supports
statement creates a pending port
.
Often supports
are used to describe pin mappings on a processor (e.g. a i2c
peripheral can map to pins here or here). The support mechanism is very flexible and can make arbitrarily complex mapping constraints.
The supports
statement is valid in the following contexts:
Signature
; Single Option Form
supports <TYPE> :
<REQUIRE-1>
...
<TYPE>.<PORT-1> => <ASSIGN-1>
<TYPE>.<PORT-2> => <ASSIGN-2>
...
; Multiple Option Form
supports <TYPE> :
<REQUIRE-1>
...
option:
<REQUIRE-1>
...
<TYPE>.<PORT-1> => <ASSIGN-1>
<TYPE>.<PORT-2> => <ASSIGN-2>
...
option:
<REQUIRE-1>
...
<TYPE>.<PORT-1> => <ASSIGN-3>
<TYPE>.<PORT-2> => <ASSIGN-4>
...
...
<TYPE>
- Bundle type that identifies what type of pending port will be constructed<REQUIRE-1>
- Optionalrequire
statements that can be used to created nested relationships.<TYPE>.<PORT-1>
- Target statement for one of theSinglePin
ports of<TYPE>
.<ASSIGN-1>
- A ref to aabstract port
, component/module port, or- The
<TYPE>.<PORT-1> => <ASSIGN-1>
statements areassignments
.
Overview
The supports
statement provide a means of constructing a pending port
. A pending port on a component or module instance is used to satisfy require
statement. A pending port isn't an object or instance like a port
, abstract port
, or net
. It is more ethereal. It provides a means of defining the constraints for making a particular connection as opposed to being a particular port or pin.
Supports Example
pcb-bundle reset:
port p
pcb-component mcu:
pin-properties:
[pin:Ref | pads:Int ...]
[RESET_n | 5 ]
supports reset:
reset.p => self.RESET_n
In this case, the support reset:
statement is constructing a single pending port of type reset
. It has one port with only one matching option, self.RESET_n
. This support reset:
statement acts like an interface definition for the reset signal.
Option Statements
The option
statement is a mechanism by which we can define a set of acceptable options for this bundle type. You can think of a supports
with option
statements as a big element-wise OR
gate.
Option Example
pcb-bundle gpio:
port p
pcb-component mcu:
port PA : pin[16]
supports gpio :
option :
gpio.p => self.PA[1]
option :
gpio.p => self.PA[4]
This supports
statement constructs a single pending port
of type gpio
. This pending port
can map to either:
self.PA[1]
ORself.PA[4]
In this case, there are only 2 options, but there is no limit. We could add an arbitrary number of option
statements.
An Arbitrary Number You Say...
"An arbitrary number seems nice but wouldn't that get rather tedious?" - well yes, but actually no.
This is where typical programming control flow and loop constructs, like for
, if
, and while
, come in. If we take our previous example and expand it from 2 options to 16 options, it might look something like this:
pcb-bundle gpio:
port p
pcb-component mcu:
val num-pins = 16
port PA : pin[ num-pins ]
supports gpio :
for i in 0 to num-pins do:
option:
gpio.p => self.PA[i]
Here - again - we only get 1 gpio
pending port from this statement. But now that one gpio can use any of the available IO pins on the PA
port of the microcontroller. Progress - now let's open it up a bit more...
Less is More
If we take one more crack at this example and expand our desire from 1 gpio
pending port to 16 gpio
pending ports with full pin-assignment across the port, we might end up here:
pcb-bundle gpio:
port p
pcb-component mcu:
val num-pins = 16
port PA : pin[ num-pins ]
for i in 0 to num-pins do:
supports gpio :
gpio.p => self.PA[i]
We don't actually need the option:
statement at all to achieve our goal. Just constructing 16 supports gpio:
statements is sufficient to create a full cross-bar. Lets consider an example of how this gets used:
inst host : mcu
require switches:gpio[4] from host
This statement basically says, "Give me 4 gpio ports - I don't care which ones right now, we'll decide that later. They just need to be GPIO ports."
This basically makes an implicit OR
between all of the defined gpio
type pending ports
that aren't used for some other purpose.
Don't Forget All the Ports
Up to now we've been talking about bundles with a single port, but we can implement supports
statements on arbitrarily complex bundles. The key things to remember are:
- Every port of a bundle must have an assignment to form a valid
supports
statement. - In each
option:
statement, every port of a bundle must have an assignment to form a validoption
statement.
Invalid Support Example
Consider the following as an example that breaks this rule:
pcb-bundle spi:
port sclk
port poci
port pico
pcb-component mcu:
port PB : pin[16]
supports spi:
spi.sclk => self.PB[0]
option:
spi.poci => self.PB[1]
spi.pico => self.PB[2]
option:
spi.poci => self.PB[3]
spi.pico => self.PB[4]
On its face, this looks like a very reasonable structure, but unfortunately it doesn't follow the signatures defined above. The likely goal of this statement is spi.sclk => self.PB[0]
for all options. We can implement that logic with explicit statements in each option:
Correct Implementation
supports spi:
option:
spi.sclk => self.PB[0]
spi.poci => self.PB[1]
spi.pico => self.PB[2]
option:
spi.sclk => self.PB[0]
spi.poci => self.PB[3]
spi.pico => self.PB[4]
Adding Properties on Assignment
It is often useful to annotate pins that are selected for a particular support function. To support this, properties can be supplied in either the supports
statement or the option
statements:
pcb-bundle gpio:
port p
pcb-bundle i2c:
port sda
port scl
pcb-component mcu:
val num-pins = 16
port PA : pin[ num-pins ]
for i in 0 to num-pins do:
supports gpio :
gpio.p => self.PA[i]
property(self.PA[i].is-gpio?) = true
supports i2c:
option:
i2c.sda => self.PA[4]
i2c.scl => self.PA[5]
property(self.PA[4].is-i2c?) = true
property(self.PA[5].is-i2c?) = true
option:
i2c.sda => self.PA[11]
i2c.scl => self.PA[12]
property(self.PA[11].is-i2c?) = true
property(self.PA[12].is-i2c?) = true
The property
statement at the end of the support/option statement only activates if this support/option is selected by the pin assignment solver. Note that this action does not necessarily happen at compile time. It may happen days or weeks later when a component in the layout moves or a route is completed.
Modules Work Too!
Up to now, we've primarily been referencing components, but don't forget that these techniques apply to pcb-module
as well. In fact, modules are where pin assignment can really shine.
Whenever you have multiple components that have a strict constraint between them, use of supports
statements in the module can help abstract the details of these complex constraints. They make our code more readable and grok-able.
Let's consider an example where we want to make multiple simple RC filters. Here is a module definition for an RC filter.
This example is intentionally simplified and does not contain a lot of the detailed engineering you might want in a real circuit, like voltage specs, tolerances, etc. This is primarily for demonstration purposes.
pcb-module low-pass (freq:Double, R-val:Double = 10.0e3) :
port vin : pin
port vout : pin
port gnd : pin
val C-val = 1.0 / ( 2.0 * PI * R-val * freq)
val C-val* = closest-std-val(C-val, 0.2)
inst R : chip-resistor(R-val)
inst C : ceramic-cap(C-val*)
net (vin, R.p[1])
net (R.p[2], C.p[1], vout)
net (C.p[2], gnd)
This filter has a 3-pin interface: vin
, vout
, and gnd
We want to construct a module that allows us to instantiate some number of these filters, but not necessarily lock ourselves into a specific pin assignment. This is where the supports
statements come in at the module level.
We do need to make sure that the input and output pins match though. It would do us no good if the input for channel 1 matched to the output of channel 3. This is where pass-through
bundle comes into play:
pcb-bundle pass-through:
port A : pin
port B : pin
pcb-module multi-lpf (ch:Int, freq:Double) :
port gnd : pin
inst lpfs : low-pass(freq)[ch]
for i in 0 to ch do:
net (gnd, lpfs[i].gnd)
for i in 0 to ch do:
supports pass-through:
pass-through.A => lpfs[i].vin
pass-through.B => lpfs[i].vout
The pass-through
bundle defines two ports that are matched. These are our vin/vout
pair.
Then we construct a pending port of type pass-through
for each channel of the multi-lpf
definition. With this construction, we get the ability to use the RC filters in any channel location on the board.
Note that the above video just shows the first solution that the Pin Assignment solver was able to deduce. You can route to any valid pin as defined by the require/supports
statements of the pin assignment problem.
Here is a link to a complete code example
Nested Require/Support Statements
To make complex constraints, we will often use a cascade of supports statements. We generally call this a Nested
require/supports statement. This structure allows us to break down a complex constraint into several simpler constraints and combine them together.
public val UART:Bundle = uart([UART-RX UART-TX UART-RTS UART-CTS])
public pcb-component my-component :
port PA : pin[10]
; Internal (Private) Bundle Definition
pcb-bundle io-pin : (port p)
for i in 5 to 10 do:
supports io-pin :
io-pin.p => PA[i]
supports UART :
require pins:io-pin[4]
UART.tx => pins[0].p
UART.rx => pins[1].p
UART.rts => pins[2].p
UART.cts => pins[3].p
Our goal is to create a UART
pending port that uses any of PA[5]
, PA[6]
, PA[7]
, PA[8]
, or PA[9]
as any of the tx
, rx
, rts
, or cts
pins of our UART. This is combinatorics problem.
We accomplish this in two steps:
- Define the set of pins from
PA
that we can select from. - Select from that set to full fill one UART
pending port
We create two kinds of supports
statements:
io-pin
- This is a private bundle type that only exists withinmy-component
.- This defines the set of pins from
PA
that we can select from.
- This defines the set of pins from
UART
- Customized UART bundle with our specific pin configuration.- This implements the "Select N from K" using a nested
require
statement.
- This implements the "Select N from K" using a nested
Symbol
The symbol
statement defines the mapping between a component's ports and the pins of a schematic symbol.
A pcb-component can have one or more schematic symbols associated with it. For the case where multiple schematic symbols are associated with a component, we consider each distinct schematic symbol a "Unit". For the single schematic symbol the "unit" connotation is implied.
Signature
; Single Symbol Unit Declaration
symbol = <SYMB>( <PORT-1> => <SYMB>.<PIN-1>, <PORT-2> => <SYMB>.<PIN-2>, ... )
; Multi-Symbol Unit Declaration
symbol :
unit(<BANK-ID>) = <SYMB>(
<PORT-1> => <SYMB>.<PIN-1>,
<PORT-2> => <SYMB>.<PIN-2>,
...
)
...
; With a Pin Property Table
assign-symbol(<SYMB>)
assign-symbols([
<BANK-ID> => <SYMB-1>,
<BANK-ID> => <SYMB-2>,
...
])
symbol =
- This is an explicit mapping statement for a single schematic symbol unit. Notice that there is no<BANK-ID>
in this declaration as the unit is implied.<SYMB>
- This is a pcb-symbol definition.<PORT-1>
- This is a port on the currentpcb-component
definition by ref.<PIN-1>
- This is a pin of the<SYMB>
definition. Note that we usedot
notation to access this pin definition.=>
is a mapping operator
symbol:
- This is a method of providing multiple schematic symbol units for this component definition via explicit mapping.- The
<BANK-ID>
is typically of typeInt|Ref
. It is used to uniquely identify a particular schematic symbol unit.- In Altium - you would like see a fixed sub-symbol identifier as
A
,B
, etc. - In JITX - you can identify units as a number
0
,1
, etc or as aRef
symbol such aspower
,config
, etc
- In Altium - you would like see a fixed sub-symbol identifier as
- Notice that for each
unit()
statement we use the same mapping syntax as for the single unit case.
- The
assign-symbol()
- This is a utility function for constructing the mapping when the user has defined a pin-properties table.- The
<SYMB>
argument is a pcb-symbol definition.
- The
assign-symbols()
- Similar toassign-symbol
but this handles the multiple symbol unit case.- This function expects a tuple of mappings between the
<BANK-ID>
and the<SYMB>
- This function expects a tuple of mappings between the
Usage
There are two primary ways to utilize the symbol
statement:
- Explicit Mapping
- Pin Properties Table Mapping
Explicit Mapping
pcb-component op-amp :
port supply : power
port vin : diff-pair
port vout : pin
symbol = op-amp-sym(
self.vin.P => op-amp-sym.vin.P
self.vin.N => op-amp-sym.vin.N
self.vout => op-amp-sym.out
self.supply.vdd => op-amp-sym.v+
self.supply.gnd => op-amp-sym.v-
)
pcb-symbol op-amp-sym :
pin v- at Point(0.0, -2.0)
pin v+ at Point(0.0, 2.0)
pin vin.N at Point(-2.0, -1.0)
pin vin.P at Point(-2.0, 1.0)
pin out at Point(2.0, 0.0)
; More Symbol Geometry Here
...
In this example, we define the ports of the op-amp
component as bundles and single pin ports. These ports don't match 1:1 with the symbol's pin declarations. This is an example of a case where an explicit symbol mapping statement is required.
The mapping statements self.vin.P => op-amp-sym.in+
are on single pins only. There is no way to map bundle to bundle between component port and symbol pins. If you were to try and do something like:
symbol = op-amp-sym(
self.vin => op-amp-sym.vin
...
)
You would see an exception like this:
Uncaught Exception: ... : Must map to a symbol pin with a single pin (received a pin bundle).
Further - this mapping function has the following expectations:
- Each component port must have a mapping
- If you were to forget a port in the mappings, this would elicit a
Uncaught Exception: ... : Every component pin must have an entry in a symbol mapping.
error.
- If you were to forget a port in the mappings, this would elicit a
- Each port to symbol mapping must be unique.
- If you were to make two mappings that both referenced
self.vout
, this would elicit aUncaught Exception: ... : A component pin is used multiple times in a mapping
error.
- If you were to make two mappings that both referenced
- Each schematic symbol pin is used in only one mapping.
- If you were to make two mappings with different component ports that each mapped to the same schematic symbol pin - this would elicit a
Uncaught Exception: ... : A symbol pin is used multiple times in a symbol mapping.
error.
- If you were to make two mappings with different component ports that each mapped to the same schematic symbol pin - this would elicit a
Pin Properties Table
With the pin-properties table, we can simplify this mapping application by using the assign-symbol() command.
pcb-component op-amp :
pin-properties:
[pin:Ref | pads:Int ...]
[v- | 1 ]
[v+ | 2 ]
[in+ | 3 ]
[in- | 4 ]
[out | 5 ]
assign-symbol(op-amp-sym)
pcb-symbol op-amp-sym :
pin v- at Point(0.0, -2.0)
pin v+ at Point(0.0, 2.0)
pin in+ at Point(-2.0, -1.0)
pin in- at Point(-2.0, 1.0)
pin out at Point(2.0, 0.0)
; More Symbol Geometry Here
...
In this example, we are leveraging the "Pin Properties" table to construct the port to symbol mapping automatically.
- Notice how we haven't defined any port statements for this component. The
pin-properties
statement handles defining any missing ports. - Notice that the
pin
column in the table specifiesref
symbols. Theseref
symbols must match with apin
in thepcb-symbol
definition. This must be a 1:1 mapping.
Assuming that the ports
that you want to define on the component match the pins
on the pcb-symbol
, this can be a convenient way to reduce duplication in the pcb-component
definition.
Symbols are Internal to Components
The pcb-symbol
assigned via the symbol
statement is only used internally by the pcb-component
definition. It is typically not possible nor useful for external entities to reference the symbol.
Further - there is no concept of "connecting" to a pcb-symbol
. When constructing a circuit (net list) with the net statement, we can't connect to a pin
of the pcb-symbol
. We must connect to a port
of the component.
Multi-part Symbols
It is often useful to create symbols that have multiple constituent parts. For example, a dual operational amplifier like the LM358LV contains two independent operational amplifiers. JITX provides multi-part symbol support via the unit
statement.
Unit Statement Example
Complete design example for multi-part symbols
public defstruct OpAmpBank :
in+:JITXObject
in-:JITXObject
out:JITXObject
public defn make-multi-opamp-symbol (banks:Seqable<OpAmpBank>, VCC:Pin, VEE:Pin) :
inside pcb-component:
symbol :
val psym = ocdb/utils/symbols/power-supply-sym
unit(0) = psym(VCC => psym.vs+, VEE => psym.vs-)
for (bank in banks, i in 1 to false) do :
val sym = ocdb/utils/symbols/multi-op-amp-sym
unit(i) = sym(
in+(bank) => sym.vi+,
in-(bank) => sym.vi-,
out(bank) => sym.vo
)
Notice that the unit
statement takes a single Int
argument as the index into the multi-part symbol array. You can then assign any symbol with the =
assignment operator.
; Example use `make-multi-opamp-symbol`
;
pcb-component DualOpAmp:
port VCC
port VEE
port in1+
port in1-
port out1
port in2+
port in2-
port out2
...
val banks = [
OpAmpBank(in1+, in1-, out1),
OpAmpBank(in2+, in2-, out2)
]
make-multi-opamp-symbol(banks, VCC, VEE)
When invoked, this results in a component with 3 parts: a sub-part symbol for the power rails and 2 sub-parts for the op-amp symbols.
Complete listing for Multi-part Symbol Example
; Generated by JITX 2.20.0
#use-added-syntax(jitx)
defpackage main :
import core
import jitx
import jitx/commands
; Define the shape/size of the board
val board-shape = RoundedRectangle(30.0, 18.5, 0.25)
public defstruct OpAmpBank :
in+:JITXObject
in-:JITXObject
out:JITXObject
public defn make-multi-opamp-symbol (banks:Seqable<OpAmpBank>, VCC:Pin, VEE:Pin) :
inside pcb-component:
symbol :
val psym = ocdb/utils/symbols/power-supply-sym
unit(0) = psym(VCC => psym.vs+, VEE => psym.vs-)
for (bank in banks, i in 1 to false) do :
val sym = ocdb/utils/symbols/multi-op-amp-sym
unit(i) = sym(
in+(bank) => sym.vi+,
in-(bank) => sym.vi-,
out(bank) => sym.vo
)
pcb-component DualOpAmp :
port VCC
port VEE
port in1+
port in1-
port out1
port in2+
port in2-
port out2
val banks = [
OpAmpBank(in1+, in1-, out1),
OpAmpBank(in2+, in2-, out2)
]
make-multi-opamp-symbol(banks, VCC, VEE)
val soic = ocdb/utils/landpatterns/soic127p-landpattern(8)
landpattern = soic(
VCC => soic.p[8], VEE => soic.p[4],
out1 => soic.p[1], in1- => soic.p[2], in1+ => soic.p[3],
out2 => soic.p[7], in2- => soic.p[6], in2+ => soic.p[5],
)
; Module to run as a design
pcb-module my-design :
; define some pins/ports
port gnd
port power-5v
port signal
inst opa : DualOpAmp
defn setup-design (name:String, board:Board
--
rules:Rules = ocdb/utils/defaults/default-rules
vendors:Tuple<String|AuthorizedVendor> = ocdb/utils/design-vars/APPROVED-DISTRIBUTOR-LIST
quantity:Int = ocdb/utils/design-vars/DESIGN-QUANTITY) :
set-current-design(name)
set-board(board)
set-rules(rules)
set-bom-vendors(vendors)
set-bom-design-quantity(quantity)
; Set the design name - a directory with this name will be generated under the "designs" directory
; the board - a Board object
; [optional] rules - the PCB design rules (if not givn default rules will be used)
; [optional] vendors - Strings or AuthorizedVendors (if not give default vendors will be used)
; [optional] quantity - Minimum stock quantity the vendor should carry (if not give default quantity will be used)
setup-design(
"jitx-design",
ocdb/utils/defaults/default-board(ocdb/manufacturers/stackups/jlcpcb-jlc2313, board-shape)
)
; Set the schematic sheet size
set-paper(ANSI-A)
; Set the top level module (the module to be compile into a schematic and PCB)
set-main-module(my-design)
; View the results
view-board()
view-schematic()
view-design-explorer()
Enum Statements
pcb-enum
is a way to store categorical variables.
Example
Right now we need to use a fully qualified name to define a new pcb-enum
. So if we wanted an enum named:
Antenna
And we're defining it in a package named
defpackage ocdb/components/espressif/esp32-wroom-32
Then the enum needs to be defined as follows:
public pcb-enum ocdb/components/espressif/esp32-wroom-32/Antenna :
All together this is what it looks like to define and use a
pcb-enum
:
defpackage ocdb/components/espressif/esp32-wroom-32:
...
public pcb-enum ocdb/components/espressif/esp32-wroom-32/Antenna :
Integrated
UMCX
; Using the pcb-enum as a type to define land pattern geometry
pcb-landpattern esp32-wroom (antenna-type:Antenna) :
switch(antenna-type):
Integrated :
package-y = 25.5
offset = (25.5 - 19.2) / 2.0
UMCX :
package-y = 19.2
offset = 0.0
Length of Enum
Get the length of an enum
When you create a new enum, called my-enum
, Stanza (the language JITX uses) creates a new integer which represents the length of that enum, called my-enum-length
.
Just add a -length
to the end of any enum name to get its length.
Geometry Statements
Geometry statements are used to draw objects in JITX.
Shape
public deftype Shape <: Equalable & Hashable
Statement | Description |
---|---|
circle | Circle |
line | Line |
point | Point |
polygon | Polygon |
rectangle | Rectangle |
text | Text |
union | Union |
Circle
public defstruct Circle <: Shape :
center: Point
radius: Double with: (ensure => non-negative!)
public defn Circle (x:Double, y:Double, radius:Double) :
Circle(Point(x,y), radius)
public defn Circle (anchor:Anchor, x:Double, y:Double, radius:Double) :
val [vt, hr] = components(anchor)
val x* = match(hr) :
(hr:W) : x + radius
(hr:C) : x
(hr:E) : x - radius
val y* = match(vt) :
(vt:S) : y + radius
(vt:C) : y
(vt:N) : y - radius
Circle(Point(x*,y*), radius)
public defn Circle (anchor:Anchor, radius:Double) :
Circle(anchor, 0.0, 0.0, radius)
public defn Circle (radius:Double) :
Circle(C, 0.0, 0.0, radius)
Circle(Point(1.0, 3.0), 1.5)
Circle(1.0, 3.0, 1.5)
Circle(C, 1.0, 3.0, 1.5)
Circle(C, 5.0)
Circle(5.0)
Line
public defstruct Line <: Shape :
width: Double with: (ensure => non-negative!)
points: Tuple<Point> with: (ensure => min-length!(2))
with:
constructor => #Line
public defn Line (width:Double, pts:Seqable<Shape|Seqable>) :
val pts* = to-tuple(flatten-shapes(pts))
ensure-points(pts*, "line")
#Line(width, pts* as Tuple<Point>)
Line(0.15, [
Point(2.0, 2.0),
Point(-2.0, 2.0),
Point(-2.0, -2.0),
Point(2.0, -2.0)])
Point
public defstruct Point <: Shape :
x: Double with: (updater => sub-x)
y: Double with: (updater => sub-y)
Point(5.0, 1.0)
Polygon
public defstruct Polygon <: Shape :
points: Tuple<Point> with: (ensure => min-length!(3))
with:
constructor => #Polygon
public defn Polygon (pts:Seqable<Point|Seqable>) :
val pts* = to-tuple(flatten-shapes(pts))
ensure-points(pts*, "polygon")
#Polygon(pts* as Tuple<Point>)
Polygon([
Point(2.0, 2.0),
Point(-2.0, 2.0),
Point(-2.0, -2.0),
Point(2.0, -2.0)])
Rectangle
public defstruct Rectangle <: Shape :
width: Double with: (ensure => non-negative!)
height: Double with: (ensure => non-negative!)
pose: Pose
public defn Rectangle (w:Double, h:Double) :
Rectangle(w, h, loc(0.0, 0.0))
public defn Rectangle (anchor:Anchor, w:Double, h:Double) :
val [vt, hr] = components(anchor)
val dx = match(hr) :
(hr:W) : w / 2.0
(hr:C) : 0.0
(hr:E) : w / -2.0
val dy = match(vt) :
(vt:S) : h / 2.0
(vt:C) : 0.0
(vt:N) : h / -2.0
Rectangle(w, h, loc(dx, dy))
Rectangle(2.0, 3.0, loc(0.0, 0.0))
Rectangle(2.0, 3.0)
Rectangle(C, 2.0, 3.0)
Text
How to create text on the silkscreen of your board and place that text anywhere you want.
The Text
function supports placing an arbitrary string on the silkscreen of your board. You can speficy the font size, the anchoring location, and position of the text.
public defstruct Text <: Shape :
string: String with: (updater => sub-string)
size: Double with: (ensure => non-negative!, updater => sub-size)
anchor: Anchor with: (updater => sub-anchor)
pose: Pose with: (updater => sub-pose)
anchor
sets the anchor for the text object. i.e. is the origin centered (C), south (S), west (W), east (E), or north (N)? NE, NW, SE, SW are also options.
Text("Sample", 2.0, C, loc(1.0, 1.0))
Union
public defstruct Union <: Shape :
shapes: Tuple<Shape>
with:
constructor => #Union
public defn Union (ss:Seqable<Shape|Seqable>) :
val ss* = generate<Shape> :
let loop (s:Shape|Seqable = ss) :
match(s) :
(s:Union) : do(yield, shapes(s))
(s:Shape) : yield(s)
(s:Seqable) : do(loop, s)
if empty?(ss*) :
fatal("Cannot form union from no shapes.")
else :
val s0 = next(ss*)
if empty?(ss*) : s0
else : #Union(to-tuple(cat([s0], ss*)))
Union([Circle(1.0), Rectangle(4.0, 0.5)])
Copper
The copper
statement defines geometry on a specified copper layer of the board stackup.
This statement can be used in the following contexts:
- pcb-landpattern
- geom statements in a pcb-module
Signature
copper(<LAYER-INDEX>) = <Shape>
<LAYER-INDEX>
- This expects a LayerIndex instance to select which copper layer to apply the created geometry.<Shape>
- The geometric Shape to create in this copper layer.
Usage
That are two contexts in which the copper
statement can be used. In each context, the usage is slightly different.
Land Pattern Context
In the pcb-landpattern
context, the copper
statement can be used standalone. This statement is used to create copper structures like an antenna in a landpattern. For example, see the ant-2GHz4-inverted-f-geom land pattern - here is an excerpt:
pcb-landpattern ant-2GHz4-inverted-f-geom :
pad launch : smd-pad(0.5, 0.5) at loc(0.0, 0.25)
pad gnd : smd-pad(0.9, 0.5) at loc(-2.1, 0.25)
copper(LayerIndex(0)) = Line(0.5, [Point(-2.1, 5.15), Point(2.2, 5.15), Point(2.2, 2.51),
Point(4.7, 2.51), Point(4.7, 5.15), Point(6.9, 5.15),
Point(6.9, 2.51), Point(9.4, 2.51), Point(9.4, 5.15),
Point(11.6, 5.15), Point(11.6, 0.96)])
copper(LayerIndex(0)) = Line(0.5, [Point(0.0, 0.0), Point(0.0, 5.15)])
copper(LayerIndex(0)) = Rectangle(0.9, 5.4, loc(-2.1, 2.7))
...
This will generate a shape that looks like this:
In the following image the 2nd and 3rd copper
statements are drawn in a different color to show their position in relation to the pads of this landpattern:
Geom/Module Context
In the pcb-module
context, the copper
statement can be used from within a geom statement. The geom
statement provides the net to which this copper geometry will be assigned. The most common usage of this statement is defining ground planes or similar geometry with explicit shapes:
val board-shape = Rectangle(30.0, 40.0)
pcb-module top-level:
...
geom(GND):
val sh = offset{_, -1.0} $ RoundedRectangle(30.0, 10.0, 3.0)
copper(LayerIndex(0, Bottom)) = loc(0.0, -15.0) * sh
Notice the use of offset to shrink the RoundedRectangle
shape by 1mm. The loc function is used to transform the shrunken shape into position in the lower half of the PCB.
Constructs a shape on the bottom layer with geometry like this:
Syntax
geom(my-net):
copper(LayerIndex(0)) = Circle(0.0, 15.0, 1.0)
copper(LayerIndex(1)) = Line(1.0, [Point(0.0, -5.0) Point(5.0, -5.0)])
Description
copper(LayerIndex(0)) = Circle(0.0, 15.0, 1.0)
Create a copper circle at (x,y) = (0,15mm) of radius 1mm. You can use any Shape with thecopper
statement.copper(LayerIndex(1)) = Line(1.0, [Point(0.0, -5.0) Point(5.0, -5.0)])
Create a 1mm wide copper line (results in a trace) on the 2nd copper layer from the top.
Copper Pour
copper-pour
is an ESIR statement for creating a filled outline on a copper-layer of our board. It gets exported to a plane in our CAD file.
Syntax
geom(my-net) :
copper-pour(LayerIndex(0), isolate = 0.1) = Circle(0.0, 15.0, 1.0)
copper-pour(LayerIndex(1), isolate = 0.1, rank = 1) = Rectangle(5.0, 5.0)
copper-pour(LayerIndex(1), isolate = 0.1, rank = 1, orphans = true) = Rectangle(5.0, 5.0)
Description
copper-pour(LayerIndex(0), isolate = 0.1) = Circle(0.0, 15.0, 1.0)
Create a circularcopper-pour
on the top layer with 0.1mm geometric clearance/isolation.isolate
is a required parameter for copper-pours. You can use any Shape with thecopper-pour
statement.copper-pour(LayerIndex(1), isolate = 0.1, rank = 1) = Rectangle(5.0, 5.0)
Create a rectangular copper-pour on the second layer form the top, with 0.1mm clearance, and rank 1. Rank is an optional parameter that increases the priority of the pour - a higher-ranked pour fills before a lower ranked one.copper-pour(LayerIndex(1), isolate = 0.1, rank = 1, orphans = true) = ``Rectangle(5.0, 5.0)
Similar to the previous command, but enable orphaned islands of copper to exist in the pour.
Place - how to place a instance or module in JITX
In JITX, we can programmatically place instances or modules on our board with a single line of code.
First, we specify place
and the instance that we want to place and then we specify the location. Finally, we specify if we want to place it on the top or the bottom of the board.
Example placement
inst c : my-component
place(c) at loc(2.0, 3.0) on Top
loc
The loc
statement is for defining a location for placement in JITX. It creates an object describing translation and rotations that can be used to create geometric constraints.
loc
statements can be multiplied together (i.e. the transforms can be composed) and used to create trees of kinematic constraints.
pcb-landpatterns
and pcb-modules
have a right-handed coordinate system with origin at loc(0.0, 0.0, 0.0)
. Items you place in that coordinate frame will move together. e.g. you can move a landpattern and all the pads stay in the same relative positions. You can place instances in a module, and the relative positions of those instances will remain fixed when you place the module.
Syntax
loc(1.0, 5.0)
loc(2.0, 3.0, 90.0)
loc(0.0, (- 3.0), (- 90.0)) on Bottom
loc(2.0, 3.0) on Top (relative-to proc)
inside pcb-landpattern :
pad p[0] : smd-pad(2.0, 1.0) at loc(0.0, 0.0, 90.0) * loc(2.0, 3.0)
pad p[1] : smd-pad(5.0, 1.0) at loc(2.0, 3.0) * loc(0.0, 0.0, 90.0)
inside pcb-module :
inst c : my-component
place(c) at loc(2.0, 3.0) on Top
Description
loc(1.0, 5.0)
Create a pose of (x,y) = (1.0mm, 5.0mm)loc(2.0, 3.0, 90.0)
Create a pose of (x,y, theta) = (1.0mm, 5.0mm, 90 degrees)loc(2.0, 3.0, (- 90.0)) on Top
Create a pose of (x,y, theta) = (1.0mm, 5.0mm, -90 degrees), specifying that the component is on Top of the board.loc(0.0, (- 3.0), (- 90.0)) on Bottom
Create a pose of (x,y, theta) = (0.0mm, -3.0mm, -90 degrees), specifying that the component is on the Bottom of the board.loc(2.0, 3.0) on Top (relative-to proc)
Create a pose of (x,y) = (2.0mm, 3.0), defined from the origin of an instance named 'proc'. This is a relative constraint.
pad p[0] : smd-pad(2.0, 1.0) at loc(0.0, 0.0, 90.0) * loc(2.0, 3.0)
pad p[1] : smd-pad(5.0, 1.0) at loc(2.0, 3.0) * loc(0.0, 0.0, 90.0)
These statements create and place two pads at locations found by composing several poses together. The ordering of poses matters when you are composing them.
The pose loc(0.0, 0.0, 90.0) * loc(2.0, 3.0)
first rotates the coordinate system by 90 degrees, and then translates it by (x,y) = (2.0mm, 3.0mm) in the new frame, so that the final pose is (x,y,theta) = (-3.0, 2.0, 90.0)
The pose loc(2.0, 3.0) * loc(0.0, 0.0, 90.0)
first translates by (x,y) = (2.0mm, 3.0mm), then rotates the coordinate system by 90 degrees, so that the final pose is (x,y,theta) = (2.0, 3.0, 90.0). Here is the resulting pad geometry (origin of the land pattern is the blue cross):
inst c : my-component
place(c) at loc(2.0, 3.0) on Top
Via [Deprecated]
The via
statement defines a multi-layer via in a landpattern or the board according to the stackup.
This statement can be used in the following contexts:
- pcb-landpattern
- geom statements in a pcb-module
Signature
via(<VIA-DEF>) at <Point>
via(<VIA-DEF>, "Key-1" => "Value-1", ... ) at <Point>
; Deprecated Form
via(
<LayerIndex-1>, <LayerIndex-2>,
type = <ViaType>
radius = <Double>
hole-radius = <Double>
properties = [ "Key-1" => "Value-1", ... ]
) at <Point>
- Required Parameters
<VIA-DEF
- pcb-via definition for constructing the via.<LayerIndex-*>
- In the deprecated form, a start and end layer for the via definition must be supplied as the first two arguments. These arguments must be LayerIndex instances.radius
- Set the via copper land radius in mmhole-radius
- Set the radius of the via hole in mm<Point>
- This is expected to be aPoint
instance that defines the location of the constructed via in the current context's coordinate frame.
- Optional Parameters
type
- Sets the type of via as one ofTHVia|MicroVia|BlindVia
. The default value isTHVia
.properties
- Set ofKeyValue<String, String>
pairs for additional properties to set."Key-1" => "Value-1"
- Set ofKeyValue<String, String>
pairs for additional properties to set.
Usage
This statement is primarily a tool to help the importer successfully bring landpattern definitions from legacy CAD tools like Altium or Kicad.
Land Pattern Context
pcb-via default-via :
name = "Default TH"
start = LayerIndex(0, Top)
stop = LayerIndex(0, Bottom)
diameter = 0.6
hole-diameter = 0.3
type = MechanicalDrill
pcb-landpattern test:
...
via(default-via) at Point(1.0, 2.0)
From the pcb-landpattern
context, the via
statement can be used directly.
The major ramification is that vias defined with this statement don't connect to nets. We suggest that you do not use this statement when designing PCB landpatterns.
When you encounter this statement in an imported PCB landpattern, you should remove this definition and place vias manually in the board visualization tool.
Geom/Module Context
In the pcb-module
context, the via
statement can be used from within a geom statement. The geom
statement provides the net to which this via will be assigned.
pcb-module test-mod:
...
geom(my-net):
via(default-via) at Point(1.0, 2.0)
via(LayerIndex(0), LayerIndex(0, Bottom),
radius = 0.3, hole-radius = 0.125)
at Point(0.0, 0.0)
via(LayerIndex(1), LayerIndex(2),
radius = 0.3, hole-radius = 0.125,
type = BlindVia)
at Point(1.0, 0.0)
via(LayerIndex(0), LayerIndex(1),
radius = 0.1, hole-radius = 0.05,
type = MicroVia)
at Point(2.0, 0.0)
The last three examples are the deprecated form of this statement. The better way to use the via
statement is to use pcb-via definitions and use them by reference.
Land Patterns
A pcb-landpattern
statement defines the pads and associated geometry for the interface between an electrical component and the PCB. This is also known as a footprint, package, or land-pattern.
Signature
pcb-landpattern lp-name (arg1:Type1, ...) :
name = <String|False>
description = <String|False>
external-names = <Tuple<String>|False>
model3d = <Model3D>
<PAD-1>
...
<LAYER-1>
...
The expression name lp-name
uniquely identifies this via definition
in the current context.
The argument list (arg1:Type1, ...)
is optional and provides a means of
constructing parameterized landpattern definitions.
name
- This name is used in the UI as a more user friendly name. If this string is not provided then thelp-name
expression is used as the landpattern's name.description
- This string is defining more meta-data for the landpattern.external-names
- Optional tuple ofString
instances of alternate names for this landpattern.model3d
- The model3d statement expects aModel3D
instance to define the data and pose for the 3D model of this component.<PAD-1>
- A pad statement defines a electrical connection interface point.- The landpattern expects zero or more pads.
- The pad reference is part of the
pcb-symbol -> pcb-landpattern
mapping in the pcb-component definition.
<LAYER-1>
- A layer() statement defines what geometry to create on the non-copper layers.
Usage
Here is a typical land-pattern example, featuring a 3-pin SOT-23 :
pcb-landpattern Static-SOT23 :
val x0 = 1.0 ; Lead-Span spacing
val y0 = 0.95 ; Pitch
; Pad Definitions
val pdef = smd-pad(0.8, 0.5)
pad p[1] : pdef at loc((- x0), y0)
pad p[2] : pdef at loc((- x0), (- y0))
pad p[3] : pdef at loc(x0, 0.0)
; Non-Copper Geometry
layer(Courtyard(Top)) = Rectangle(3.3, 3.0)
layer(Silkscreen("f-silk", Top)) = LineRectangle(3.3, 3.0)
; Reference Designator Helper Function
ref-label()
If we attempt to visualize this landpattern:
view(Static-SOT23)
In this definition, notice that you can use standard stanza
syntax like defining immutable values (eg, val x0 = -0.95
). This can make constructing landpatterns easier because we can use arithmetic directly in the land-pattern definition.
What the >REF?
The
>REF
text in the silkscreen might be a bit jarring at first, but keep in mind that we are defining the land pattern - this isn't what is going to be in the resultant board. The>REF
will get replaced by a reference designator and that designator will be positioned automatically.
Pad Definitions
The definition for smd-pad can be found in OCDB. This defines a typical surface mount pad. There are multiple ways of invoking smd-pad
:
; Originating Definitions
pcb-pad smd-pad (copper-layer:Shape, solder-mask-layer:Shape|False, paste-layer:Shape) :
...
; Polymorphic Functions
defn smd-pad (s:Shape) -> Pad
defn smd-pad (anchor:Anchor, width:Double, height:Double) -> Pad
defn smd-pad (d:Dims) -> Pad
defn smd-pad (width:Double, height:Double) -> Pad
The top definition is the base pcb-pad
definition and then the following functions are all different ways to create that originating definition. These are polymorphic functions.
Poly-Wut? - Polymorphism is a fancy way of saying that the compiler is going take whatever args you give for a particular function and do its level best to call the right function to give you the result you want. It may not be able to satisfy every request. This may be due to a lack of a definition that supports that modality. Or it may be due to an ambiguity in the definition or the invocation.
Big Picture - The
smd-pad
symbol encodes the core idea - eg, This function returns a surface mount pad definition. All of the polymorphic versions of this function must adhere to that core idea.
Layer Content
The next section of the pcb-landpattern
definition is the non-copper layer()
statements. This includes the courtyard, silkscreen, forbid/keepout regions, and any other pertinent content you might wish to include in the land-pattern.
The ref-label function is a generator. Here is an excerpt from the ref-label
definitions:
defn ref-label (pose:Pose, anchor:Anchor) :
inside pcb-landpattern:
val min-height = clearance(current-rules(), MinSilkscreenTextHeight)
layer(Silkscreen("values", Top)) = Text(">REF", min-height, anchor, pose)
defn ref-label () :
ref-label(loc(0.0, 0.0), C)
The inside pcb-landpattern
statement is what makes this function a generator. The lines inside this statement effectively get copied in to the parent context (in this case, the Static-SOT23
land-pattern definition).
The idea is that inserting a reference designator label into a pcb-landpattern
definition is a pretty common activity. Copying and pasting this into every pcb-landpattern
definition would get annoying fast. It also would be pretty significant violation of the "Don't Repeat Yourself" principle. The ref-label
generator allows us to inject content into the parent context without necessarily knowing when and where it will get used.
Arguments to a Land Pattern
Arguments to a landpattern can be an effective way to construct highly reusable land patterns:
pcb-landpattern header (num-pins:Int, pitch:Double = 2.54):
val pdef = pth-pad(0.9, 1.15)
for i in 1 through num-pins do:
pad p[i] : pdef at loc(to-double(i - 1) * pitch, 0.0)
...
These arguments allow us to construct an N-pin through-hole single-row header and customize its pitch. The pitch
argument has a default value making it optional.
You can find a more advanced example of pcb-landpattern
with arguments here.
Statements
Here is the list of all of the statements you can use in a pcb-landpattern
:
Statement | Description |
---|---|
description | Description for the land pattern |
external-names | Define common names for the land pattern |
layers | Define shapes on layers like SolderMask |
name | Name of the land pattern |
pad | Creates pads you can connect to |
model3d | Associate a 3D model with a landpattern |
copper | Construct unassociated copper geometry in the landpattern |
via | Construct unassociated via placements in the landpattern |
Copper
The copper
statement defines geometry on a specified copper layer of the board stackup.
This statement can be used in the following contexts:
- pcb-landpattern
- geom statements in a pcb-module
Signature
copper(<LAYER-INDEX>) = <Shape>
<LAYER-INDEX>
- This expects a LayerIndex instance to select which copper layer to apply the created geometry.<Shape>
- The geometric Shape to create in this copper layer.
Usage
That are two contexts in which the copper
statement can be used. In each context, the usage is slightly different.
Land Pattern Context
In the pcb-landpattern
context, the copper
statement can be used standalone. This statement is used to create copper structures like an antenna in a landpattern. For example, see the ant-2GHz4-inverted-f-geom land pattern - here is an excerpt:
pcb-landpattern ant-2GHz4-inverted-f-geom :
pad launch : smd-pad(0.5, 0.5) at loc(0.0, 0.25)
pad gnd : smd-pad(0.9, 0.5) at loc(-2.1, 0.25)
copper(LayerIndex(0)) = Line(0.5, [Point(-2.1, 5.15), Point(2.2, 5.15), Point(2.2, 2.51),
Point(4.7, 2.51), Point(4.7, 5.15), Point(6.9, 5.15),
Point(6.9, 2.51), Point(9.4, 2.51), Point(9.4, 5.15),
Point(11.6, 5.15), Point(11.6, 0.96)])
copper(LayerIndex(0)) = Line(0.5, [Point(0.0, 0.0), Point(0.0, 5.15)])
copper(LayerIndex(0)) = Rectangle(0.9, 5.4, loc(-2.1, 2.7))
...
This will generate a shape that looks like this:
In the following image the 2nd and 3rd copper
statements are drawn in a different color to show their position in relation to the pads of this landpattern:
Geom/Module Context
In the pcb-module
context, the copper
statement can be used from within a geom statement. The geom
statement provides the net to which this copper geometry will be assigned. The most common usage of this statement is defining ground planes or similar geometry with explicit shapes:
val board-shape = Rectangle(30.0, 40.0)
pcb-module top-level:
...
geom(GND):
val sh = offset{_, -1.0} $ RoundedRectangle(30.0, 10.0, 3.0)
copper(LayerIndex(0, Bottom)) = loc(0.0, -15.0) * sh
Notice the use of offset to shrink the RoundedRectangle
shape by 1mm. The loc function is used to transform the shrunken shape into position in the lower half of the PCB.
Constructs a shape on the bottom layer with geometry like this:
Syntax
geom(my-net):
copper(LayerIndex(0)) = Circle(0.0, 15.0, 1.0)
copper(LayerIndex(1)) = Line(1.0, [Point(0.0, -5.0) Point(5.0, -5.0)])
Description
copper(LayerIndex(0)) = Circle(0.0, 15.0, 1.0)
Create a copper circle at (x,y) = (0,15mm) of radius 1mm. You can use any Shape with thecopper
statement.copper(LayerIndex(1)) = Line(1.0, [Point(0.0, -5.0) Point(5.0, -5.0)])
Create a 1mm wide copper line (results in a trace) on the 2nd copper layer from the top.
Description
The description
statement is the optional descriptive field of a JITX object. Use it to store a description of the object for human designers reading JITX, and to make the object easier to find via text search. This description also shows up in the UI, such as the design explorer, to provide more insight into particular components and modules in the design.
Signature
description = <String|False>
This statement is optional. The default value will be false
in case no description
statement is found in a definition.
Any string is a valid description
value.
Each JITX definition may have exactly one description
statement. If more than one statement is encountered in a definition, then a DuplicateCStmtError
exception will be raised.
Examples
; We can use string literals to describe a particular component.
pcb-component analog-devices-ADM7150 :
description = "800 mA Ultralow Noise, High PSRR, RF Linear Regulator"
; We can use string formatting to construct descriptions based on variables
; or arguments to a JITX Definition
pcb-module band-pass-filter (high-cut:Double, low-cut:Double) :
description = to-string(
"Band-pass Filter - Highpass = %_ Hz and Lowpass = %_ Hz." % [high-cut, low-cut]
)
External Names
external-names
is an optional field in a pcb-landpattern
to allow us to match the land pattern with parts found at component vendors.
Syntax
external-names = (["0402"])
external-names = (["0402" "0402-01005"])
Description
external-names = (["0402"])
Indicate this land pattern matches "0402"-sized SMD componentsexternal-names = (["0402" "0402-01005"])
Indicate this land pattern matches "0402" and "0402-01005"-sized SMD components. Each additional string is another alias.
Layer
The layer
statement is used to create geometry on the non-copper layers of a circuit board. The layer()
statement is valid in the following contexts:
Signature
layer(<LayerSpecifier>) = <Shape>
<LayerSpecifier>
- A LayerSpecifier instance that identifies which non-copper layer to apply the provided geometry to.<Shape>
- AShape
instance that defines the geometry that will be created on the specified layer.
Usage
The most common usage of the layer()
statement is in pcb-landpattern:
pcb-landpattern diode-lp :
pad c : smd-pad(0.4, 0.75) at loc(-1.25, 0.0) on Top
pad a : smd-pad(0.4, 0.75) at loc(1.25, 0.0) on Top
layer(Silkscreen("body", Top)) = LineRectangle(1.8, 1.0)
layer(Silkscreen("body", Top)) = Line(0.1, [Point(-0.70, -0.5), Point(-0.70, 0.5)])
layer(Courtyard(Top)) = Rectangle(3.2, 2.0)
layer(ForbidCopper(LayerIndex(0))) = Rectangle(2.0, 1.0)
This will construct a landpattern that looks like this:
Notice the silkscreen in yellow with cathode marker. The blue box is the ForbidCopper
layer on the Top Layer. Red is the top copper pads for the cathode c
and anode a
.
The white bounding rectangle is the Courtyard
layer.
See LayerSpecifier for more information about specific layers.
Cutouts
When constructing cutouts in the board layout, your best bet is to use a solid region as opposed to a line. A line can confuse the routing engine into thinking that there are two physically separate regions where copper can be placed.
Consider a USB connector part, U231-096N-3BLRT06-SS, Jing Extension of the Electronic Co.
Here is an excerpt from the datasheet:
If we draw the cutout with a line, as shown in the datasheet, we get this:
pcb-landpattern USB-conn:
...
layer(Cutout()) = Line(0.254, [Point(-7.300, -7.650), Point(-8.450, -7.650)])
layer(Cutout()) = Polyline(0.254, [
Point(6.850, 4.650)
Point(6.850, -7.650)
Point(8.450, -7.650)])
layer(Cutout()) = Polyline(0.254, [
Point(-6.850, 4.650)
Point(-6.850, -7.650)
Point(-7.300, -7.650)])
layer(Cutout()) = Line(0.254, [Point(-6.850, 4.650), Point(6.850, 4.650)])
The cutout line is in gold color. Notice that the ground layer (blue) copper is present on both sides of the cut line with some margin in between. The routing engine basically thinks that the cutout is just the line. If we were making a slot - that would probably be reasonable. But for this case, we want the hole region between the cutout line and the board edge (red) to be a cutout region.
The right way is to use a Rectangle
or Polygon
solid geometry:
pcb-landpattern USB-conn :
...
layer(Cutout()) = Polygon([
Point(-6.850, 4.650), Point(6.850, 4.650),
Point(6.850, -7.650), Point(-6.850, -7.65),
])
layer(Cutout()) = Circle(Point(-6.85 + 0.5, 4.65), 0.5)
layer(Cutout()) = Circle(Point(6.85 - 0.5, 4.65), 0.5)
Notice that the cutout region fills the entire connector region and the blue ground plane is not present in the cutout region.
Name
The name
statement is the optional name field of a JITX object. Use it to store a descriptive name as a String
. This name will often be used in the UI in place of the object's expression name for better readability.
Signature
name = <String>
This statement is optional. The default value will be the definition's symbol name in case no name
statement is found in a definition.
Any string is a valid name
value.
Usage
Literal String Names
pcb-component component :
name = "ADM7150"
pcb-module band-pass-filter :
name = "Band-pass filter"
The examples name = "ADM7150"
and name = "Band-pass filter"
use a String
literal for the name.
Formatted Strings
pcb-pad smd-pad (anchor:Anchor, w:Double, h:Double) :
name = to-string("%_x%_ %_ SMD Pad" % [w,h,anchor])
pcb-landpattern test-lp:
pad p[1] : smd-pad(C, 0.6, 0.7) at loc(x0,y0)
We can also construct strings using formatting routes and parameter arguments for a particular definition. In this example, the constructed smd-pad
name property would be 0.6x0.7 C SMD Pad
Pad
pad
statement defines an electrical connection point in a pcb-landpattern
. A pad is an object that you can associate with a component pin.
Signature
pad <REF> : <DEF> at <POSE>
pad <REF> : <DEF> at <POSE> on <SIDE>
<REF>
- The pad's ref name in thepcb-landpattern
context. Common ref names arep[1]
,p[2]
, etc. Notice the index starting at one instead of zero.<DEF>
- A reference to apcb-pad
definition. This will define the geometry and configuration of this specific pad in the land-pattern.<POSE>
- APose
instance that transforms thepcb-pad
definition into the appropriate location in thepcb-landpattern
frame of reference.<SIDE>
- ASide
instance ofTop|Bottom
that indicates whether this pad is located on the top or bottom side of the board. The default side if theon <SIDE>
expression is not provided isTop
.
Usage
The pad
statement is used as part of a pcb-landpattern
definition to define the electrical connection points between the PCB and a component. There are typically multiple pad
statements in a pcb-landpattern
definition.
Reference Examples
When defining the pad reference, we can use any dot
notation or array indexing feature:
pad a : square-pad at loc(0.0, 0.0)
pad p[0] : smd-pad(0.5, 0.5) at loc(5.0, 0.0)
pad dp.P : pth-pad(0.5, 0.9) at loc(-2.0, 0.0)
pad dp.N : pth-pad(0.5, 0.9) at loc(2.0, 0.0)
The dot
notation example dp.P
and dp.N
is particular useful when attempting to create a pcb-landpattern
that interfaces to a diff-pair
bundle component port. See pcb-component for more information.
Constructing Pads in Loops
We can use typical stanza for-loop constructions inside the pcb-landpattern
to make it easier to build many pins:
val pdef = smd-pad(0.8, 0.3)
for i in 1 through num-pins do:
val pose = loc(to-double(i) * 1.27, 0.0)
pad p[i] : pdef at pose
Notice the use of 1 through num-pins
as the sequence generator. If num-pins = 4
- then this will construct the sequence [1, 2, 3, 4]
for a one-based index.
Using on <SIDE>
When constructing a board edge connector (like a PCIe card), we often need to place pads on both side of the board:
pcb-landpattern Card-Edge (num-pins:Int):
val pdef = smd-pad(0.4, 1.6)
val pitch = 1.2
for i in 1 through num-pins do:
pad A[i] : pdef at loc( to-double(i) * pitch, 0.0) on Top
pad B[i] : pdef at loc( to-double(i) * pitch, 0.0) on Bottom
Careful with Ref Names
It is an error to create two pads with the same reference. For example, this case:
pcb-landpattern diode-lp :
pad c : smd-pad(0.4, 0.75) at loc(-1.25, 0.0) on Top
pad a : smd-pad(0.4, 0.75) at loc(1.25, 0.0) on Top
pad a : smd-pad(0.4, 0.75) at loc(1.25, 2.0) on Top
Notice the duplicate a
pad. This will throw an exception when the design is run:
Uncaught Exception: StmtTable adding duplicate record - key: a ...
Programmatically Generated Ref Names
Often times it can be useful to programmatically generate a ref name for a pad. To do this we need to use a special syntax to force the compiler to interpret a variable as a reference by value instead of a symbol:
val prefix = Ref("RP")
pad (prefix)[4] : bga-pad(1.0) at loc(1.0, 0.0)
This will construct a pad with ref RP[4]
. Notice how in this case the prefix
value is wrapped in parenthesis ()
. This forces the compiler to interpret the value of prefix
as the reference. Compare this to the case where there is no ()
:
val prefix = Ref("RP")
pad prefix[4] : bga-pad(1.0) at loc(1.0, 0.0)
In this case the pad would have the literal reference name prefix[4]
.
Via [Deprecated]
The via
statement defines a multi-layer via in a landpattern or the board according to the stackup.
This statement can be used in the following contexts:
- pcb-landpattern
- geom statements in a pcb-module
Signature
via(<VIA-DEF>) at <Point>
via(<VIA-DEF>, "Key-1" => "Value-1", ... ) at <Point>
; Deprecated Form
via(
<LayerIndex-1>, <LayerIndex-2>,
type = <ViaType>
radius = <Double>
hole-radius = <Double>
properties = [ "Key-1" => "Value-1", ... ]
) at <Point>
- Required Parameters
<VIA-DEF
- pcb-via definition for constructing the via.<LayerIndex-*>
- In the deprecated form, a start and end layer for the via definition must be supplied as the first two arguments. These arguments must be LayerIndex instances.radius
- Set the via copper land radius in mmhole-radius
- Set the radius of the via hole in mm<Point>
- This is expected to be aPoint
instance that defines the location of the constructed via in the current context's coordinate frame.
- Optional Parameters
type
- Sets the type of via as one ofTHVia|MicroVia|BlindVia
. The default value isTHVia
.properties
- Set ofKeyValue<String, String>
pairs for additional properties to set."Key-1" => "Value-1"
- Set ofKeyValue<String, String>
pairs for additional properties to set.
Usage
This statement is primarily a tool to help the importer successfully bring landpattern definitions from legacy CAD tools like Altium or Kicad.
Land Pattern Context
pcb-via default-via :
name = "Default TH"
start = LayerIndex(0, Top)
stop = LayerIndex(0, Bottom)
diameter = 0.6
hole-diameter = 0.3
type = MechanicalDrill
pcb-landpattern test:
...
via(default-via) at Point(1.0, 2.0)
From the pcb-landpattern
context, the via
statement can be used directly.
The major ramification is that vias defined with this statement don't connect to nets. We suggest that you do not use this statement when designing PCB landpatterns.
When you encounter this statement in an imported PCB landpattern, you should remove this definition and place vias manually in the board visualization tool.
Geom/Module Context
In the pcb-module
context, the via
statement can be used from within a geom statement. The geom
statement provides the net to which this via will be assigned.
pcb-module test-mod:
...
geom(my-net):
via(default-via) at Point(1.0, 2.0)
via(LayerIndex(0), LayerIndex(0, Bottom),
radius = 0.3, hole-radius = 0.125)
at Point(0.0, 0.0)
via(LayerIndex(1), LayerIndex(2),
radius = 0.3, hole-radius = 0.125,
type = BlindVia)
at Point(1.0, 0.0)
via(LayerIndex(0), LayerIndex(1),
radius = 0.1, hole-radius = 0.05,
type = MicroVia)
at Point(2.0, 0.0)
The last three examples are the deprecated form of this statement. The better way to use the via
statement is to use pcb-via definitions and use them by reference.
model3d
model3d
is a JITX statement that defines a 3D model associated with a pcb-landpattern
definition.
Syntax
; inside pcb-landpattern :
model3d = Model3D(filename, position, scale, rotation) where :
val filename = "my-3d-model.wrl"
val position = Vec3D(0.0, 0.0, 0.0)
val scale = Vec3D(0.0, 0.0, 0.0)
val rotation = Vec3D(0.0, 0.0, 0.0)
Description
The model3d
statement associates a 3D model with a pcb-landpattern
. Models may be .stp
, .step
, or .wrl
files. The arguments to Model3D
are the name of the 3D model file, and 3D vectors for position/translation, scale, and rotation.
The filename
argument may be a relative or absolute path. If it is relative, the resolved path will be relative to the directory of the source file that contains the statement. If it is started with ${PROJECT_ROOT}
(e.g. ${PROJECT_ROOT}/3DModels/model.step
), the resolved path will be relative to the project root directory.
For example :
;in project/landpatterns/landpattern.stanza
;...
model3d = Model3D("../3DModels/model.step")
The resolved path will be project/3DModels/model.step
.
During import, imported model files are placed in the imported design directory under <imported-design>/3d-models
and model3d
statements are generated automatically.
Landpattern with Arguments
After designing a few boards, it becomes apparent that while most SOT-23's are about the same, there are always some outliers. One manufacturer might claim a slightly different tolerance to another. This typically results in either a land-pattern that isn't necessarily tuned for each part (ie, one land pattern to rule them all) or a plurality of land patterns for each of those cases. Maintaining this library of land patterns quickly becomes a full-time job.
With the ability to write circuits-as-code, we open a new way to support dynamically generated land-patterns. The following is an excerpt from this complete example:
Note - This is a more complex example. But fear not stalwart electron herder, these examples will serve you well as you become more familiar with the JITX environment.
pcb-landpattern SOT-23 (pad-size:Dims, pad-grid:Dims, margin:Double = 0.2) :
val x0 = x(pad-grid)
val y0 = y(pad-grid)
; Pad Definitions
val pdef = smd-pad(pad-size)
pad p[1] : pdef at loc((- x0), y0)
pad p[2] : pdef at loc((- x0), (- y0))
pad p[3] : pdef at loc(x0, 0.0)
; Find the bounding box around all of the pads.
val outline = bounds $ Union $ for pd in pads(self) seq:
pose(pd) * pad-shape(pdef)
; Non-Copper Geometry
val outline* = offset(outline, margin) as RoundedRectangle
val courtyard = LineRectangle(width(outline*), height(outline*))
layer(Courtyard(Top)) = courtyard
layer(Silkscreen("f-silk", Top)) = courtyard
; Construct the Pin #1 marker in the silkscreen
val r = 0.15
val marker = loc((width(outline*) / -2.0) - (3.0 * r), y0)
layer(Silkscreen("f-silk", Top)) = marker * Circle(2.0 * r)
; Reference Designator Helper Function
ref-label()
If we attempt to visualize this landpattern, we should see:
view(SOT-23(Dims(0.8, 0.5), Dims(1.0, 0.95)))
You can still see the skeleton of the static SOT-23 implementation but we've added some new features:
- We've added some arguments to the
pcb-landpattern
definition that allow us to create a SOT-23 landpattern to our exact specifications. - We compute the bounding rectangle for the pads so that we can construct courtyard and silkscreen outlines dynamically. This reduces the amount of magic numbers in our design.
- We add a "Pin 1" marker that is placed outside the silkscreen boundary.
Arguments
The arguments are encoded at the beginning of the definition as if this were a normal function definition:
pcb-landpattern SOT-23 (pad-size:Dims, pad-grid:Dims -- margin:Double = 0.2) :
...
The pad-size
defines the width/height of the pads. The pad-grid
defines the "Lead Span" (X) and "Pitch" (Y) of the constructed pads. These are both required arguments.
The margin
is the extra space between the external boundary of the pads and the constructed courtyard/silkscreen outlines. This argument has a default value of 0.2
which means that it is optional argument.
Boundary Rectangle
The core functionality that makes this routine possible is the following line:
; Find the bounding box around all of the pads.
val outline = bounds $ Union $ for pd in pads(self) seq:
pose(pd) * pad-shape(pdef)
We can break this down into constituent pieces:
for pd in pads(self) seq:
This is a for-loop, like many we have seen before, but this one has something different at the end - a seq
instead of a do
. seq
is an operating function. In particular, seq
will convert all of the values returned by the body of this for-loop into a Sequence (iterator).
The pads function provides a sequence of LandPatternPad
instances - one for each of the defined pad
statements in the landpattern.
The body of the for-loop consists of:
pose(pd) * pad-shape(pdef)
This takes the shape from the pad definition pdef
and then translates it according to the Pose
of that pad instance. This translation is so that we don't end up with just 3 rectangles right on top of each other at the origin.
The resultant sequence from the for-loop is passed to the Union
shape
constructor function via the Apply Operator, the $
operator.
The Union
shape is method of constructing complex shapes. It constructs the geometric union as the name suggests.
The bounds
function is similarly invoked via the apply
operator. Note how the apply operator allows for chaining a sequence of functions on the output of the for-loop. The bounds
function computes the axis-aligned bounding box around the Union
shape of all the pads in the land pattern.
That outline
result is a Rectangle
shape that we can now use to construct our courtyard/silkscreen outline or correctly place the pin-1 marker for our component.
#use-added-syntax(jitx)
defpackage dynamic_landpatterns:
import core
import jitx
import jitx/commands
import ocdb/utils/landpatterns
import ocdb/utils/box-symbol
doc: \<DOC>
Static Version
<DOC>
pcb-landpattern Static_SOT23 :
val x0 = 1.0 ; Lead-Span spacing
val y0 = 0.95 ; Pitch
; Pad Definitions
val pdef = smd-pad(0.8, 0.5)
pad p[1] : pdef at loc((- x0), y0)
pad p[2] : pdef at loc((- x0), (- y0))
pad p[3] : pdef at loc(x0, 0.0)
; Non-Copper Geometry
layer(Courtyard(Top)) = Rectangle(3.3, 3.0)
layer(Silkscreen("f-silk", Top)) = LineRectangle(3.3, 3.0)
; Reference Designator Helper Function
ref-label()
doc: \<DOC>
Very Limited Bounding Region Calculation
<DOC>
defn bounds (sh:Union|Shape) -> Rectangle :
val extrema = to-tuple $ for s in shapes(sh as Union) seq:
match(s):
(r:Rectangle):
val w2 = width(r) / 2.0
val h2 = height(r) / 2.0
; println("W: %_ H: %_ P: %_" % [w2, h2, pose(r)] )
[pose(r) * Point(w2, h2), pose(r) * Point((- w2), (- h2))]
(other):
throw $ Exception("Unhandled Shape")
val max-x = maximum $ for obj in extrema map:
x(obj[0])
val max-y = maximum $ for obj in extrema map:
y(obj[0])
val min-x = minimum $ for obj in extrema map:
x(obj[1])
val min-y = minimum $ for obj in extrema map:
y(obj[1])
val mid-x = (max-x + min-x) / 2.0
val mid-y = (max-y + min-y) / 2.0
val w = max-x - min-x
val h = max-y - min-y
Rectangle(w, h, loc(mid-x, mid-y))
doc: \<DOC>
Dynamic Version
<DOC>
pcb-landpattern SOT-23 (pad-size:Dims, pad-grid:Dims, margin:Double = 0.2) :
val x0 = x(pad-grid)
val y0 = y(pad-grid)
; Pad Definitions
val pdef = smd-pad(pad-size)
pad p[1] : pdef at loc((- x0), y0)
pad p[2] : pdef at loc((- x0), (- y0))
pad p[3] : pdef at loc(x0, 0.0)
; Find the bounding box around all of the pads.
val outline = bounds $ Union $ for pd in pads(self) seq:
pose(pd) * pad-shape(pdef)
; Non-Copper Geometry
val outline* = offset(outline, margin) as RoundedRectangle
val courtyard = LineRectangle(width(outline*), height(outline*))
layer(Courtyard(Top)) = courtyard
layer(Silkscreen("f-silk", Top)) = courtyard
val r = 0.15
val marker = loc((width(outline*) / -2.0) - (3.0 * r), y0)
layer(Silkscreen("f-silk", Top)) = marker * Circle(2.0 * r)
; Reference Designator Helper Function
ref-label()
; view(Static_SOT23)
view(SOT-23(Dims(0.8, 0.5), Dims(1.0, 0.95)))
Materials
A pcb-material
definition represents a material in a pcb-stackup
layer.
Signature
pcb-material mat-name (arg1:Type1, ...) :
name = <String|False>
description = <String|False>
type = <MaterialType>
material-name = <String|False>
; For Dielectric Materials
dielectric-coefficient = <Double|False>
loss-tangent = <Double|False>
; For Conductor Materials
roughness = <Double|False>
The expression name mat-name
uniquely identifies this stackup definition in the current context.
The argument list (arg1:Type1, ...)
is optional and provides a means of
constructing parameterized material definitions.
- Required Parameters
type
- There are two valid values forMaterialType
Conductor
Dielectric
- Optional Parameters
name
- This name is used in the UI as a more user friendly name. If this string is not provided then themat-name
expression is used as the stackup's name.description
- This string is defining more meta-data for the material - such as manufacturer of this material.material-name
- This string label is optional and used only for Kicad Import/Export.Dielectric
Type Optional Parametersdielectric-coefficient
- Relative Coefficient of Permittivity for this material. This value is a unit-less instance of typeDouble
and generally should be 1.0 or greater.loss-tangent
- The Loss Tangent for this material. This value is a unit-less instance of typeDouble
.
Conductor
Type Optional Parametersroughness
- Surface Roughness of the conductor layer. This parameter effects the losses in RF circuits due to the skin effect. This value is in mm.
If you add roughness
to a Dielectric
type or a loss-tangent
to a Conductor
type, the JITX runtime will throw an error.
Example:
Uncaught Exception: Dielectrics can only have dielectric-coefficient and loss-tangent attributes.
Usage
The pcb-material
statements create definitions that are primarily used by the pcb-stackup statement. These material definitions provide the properties of each material in the stackup.
Below is a trivial example for a 2-layer board:
pcb-material core-45 :
type = Dielectric
dielectric-coefficient = 4.5
loss-tangent = 0.008
pcb-material soldermask :
type = Dielectric
dielectric-coefficient = 3.2
loss-tangent = 0.012
pcb-material copper:
type = Conductor
pcb-stackup simple-2-layer :
name = "2-layer 1.6mm"
stack(0.019, soldermask)
stack(0.035, copper)
stack(1.5, core-45)
stack(0.035, copper)
stack(0.019, soldermask)
Using Arguments
Often times, a family of prepreg materials will come in multiple different constructions. For example, see Isola 370HR. Here is an excerpt from their core data:
We might want to construct a pcb-material
that allows us to label with these different features:
pcb-material isola-370HR (ply:String, Dk:Double, Df:Double) :
name = to-string("Isola 370HR %_" % [ply])
type = Dielectric
dielectric-coefficient = Dk
loss-tangent = Df
val isola-370hr-1080 = isola-370HR("1x1080", 4.04, 0.021)
This structure allows us to build a material spec for any
of the cores in the Isola catalog. The name
for this material will be Isola 370HR 1x1080
which we can use in programmatically generated stackup tables.
Name
The name
statement is the optional name field of a JITX object. Use it to store a descriptive name as a String
. This name will often be used in the UI in place of the object's expression name for better readability.
Signature
name = <String>
This statement is optional. The default value will be the definition's symbol name in case no name
statement is found in a definition.
Any string is a valid name
value.
Usage
Literal String Names
pcb-component component :
name = "ADM7150"
pcb-module band-pass-filter :
name = "Band-pass filter"
The examples name = "ADM7150"
and name = "Band-pass filter"
use a String
literal for the name.
Formatted Strings
pcb-pad smd-pad (anchor:Anchor, w:Double, h:Double) :
name = to-string("%_x%_ %_ SMD Pad" % [w,h,anchor])
pcb-landpattern test-lp:
pad p[1] : smd-pad(C, 0.6, 0.7) at loc(x0,y0)
We can also construct strings using formatting routes and parameter arguments for a particular definition. In this example, the constructed smd-pad
name property would be 0.6x0.7 C SMD Pad
Description
The description
statement is the optional descriptive field of a JITX object. Use it to store a description of the object for human designers reading JITX, and to make the object easier to find via text search. This description also shows up in the UI, such as the design explorer, to provide more insight into particular components and modules in the design.
Signature
description = <String|False>
This statement is optional. The default value will be false
in case no description
statement is found in a definition.
Any string is a valid description
value.
Each JITX definition may have exactly one description
statement. If more than one statement is encountered in a definition, then a DuplicateCStmtError
exception will be raised.
Examples
; We can use string literals to describe a particular component.
pcb-component analog-devices-ADM7150 :
description = "800 mA Ultralow Noise, High PSRR, RF Linear Regulator"
; We can use string formatting to construct descriptions based on variables
; or arguments to a JITX Definition
pcb-module band-pass-filter (high-cut:Double, low-cut:Double) :
description = to-string(
"Band-pass Filter - Highpass = %_ Hz and Lowpass = %_ Hz." % [high-cut, low-cut]
)
Module Level Statements
A pcb-module
statement defines a circuit made up of component instances and the connections between the ports of each instance. The pcb-module
can be thought of as the circuit organization tool for building hierarchical circuits. In addition to defining the circuit net list, the pcb-module
can also define physical geometry and the positioning of instances in the board view with respect to the module's kinematic frame.
The pcb-module
is similar to the module in Verilog. If you are coming from VHDL, you will find that it is similar in ways to the entity concept except there is no way to provide multiple architecture
bodies for a JITX pcb-module
.
The pcb-module
statement is valid in all contexts of a JITX design.
Signature
pcb-module mod-name (arg1:Type1, ...) :
name = <String>
description = <String|False>
; Other module statements here
The expression name mod-name
uniquely identifies this module definition
in the current context.
The argument list (arg1:Type1, ...)
is optional and provides a means of constructing parameterized module definitions. The arguments in this list must be of type JITXValue
.
There are many statements associated with the pcb-module
definition. See the Statements section below for a detailed listing.
Minimal pcb-module
Definition
All statements for a pcb-module
are optional. Here is an example of the most minimal pcb-module
definition:
pcb-module minimal:
false
This is clearly not very useful - but demonstrates the point and it will compile.
One thing to notice is that indentation for the module definition's statements is required. Here the only statement is a value expression of false
.
The indentation must be in the form of [spaces]
. A tab character will cause a syntax error.
Usage
The pcb-module
definition is quite versatile. It is typically used in the following conditions:
- Defining the top-level for a circuit design. See Design Hierarchy for more info.
- Defining reusable or one-off sub-circuits in a design.
- Constructing a wrapper around a
pcb-component
instance.
Example Top-Level Design
val board-shape = RoundedRectangle(60.0, 40.0, 1.6)
pcb-module top-level :
inst MCU : RP2040
require bus:i2c from MCU
inst accelerometer : MMA8451Q
inst temp-sensor : TMP116
net GND (MCU.GND, accelerometer.rail.V-, temp-sensor.rail.V-)
net I2C (bus, accelerometer.bus, temp-sensor.bus)
...
geom(GND):
copper-pour(LayerIndex(1), isolate = 0.125, rank = 1) = board-shape
copper-pour(LayerIndex(4), isolate = 0.125, rank = 1) = board-shape
Common Traits:
- For top-level design modules, you will often find multiple
inst
statements constructing the sub-circuit and components of a design. - Top-level modules typically don't have
port
definitions. They can but it often isn't useful because the off-board interfaces are typically through connectors. - It is very common to see many
require
statements that construct the pin assignment problem for the circuit. - Top-level modules typically have
geom
statements to contruct large-scale features of a design, like ground planes and keepout regions.
Example Reusable Circuit
pcb-module LDO (comp:Instantiable, Vin:Double, Vout:Double) :
port vin : power
port vout : power
inst ldo : comp
net (vin.V+, ldo.VIN)
net (vin.V-, vout.V-, ldo.GND)
net (vout.V+, ldo.VOUT)
bypass-cap-strap(ldo.VIN, ldo.GND, 1.0e-6)
bypass-cap-strap(ldo.VOUT, ldo.GND, 1.0e-6)
; Construct Voltage divider here.
inst div : voltage-divider(typ(Vin), typ(Vout))
net (div.in, ldo.VOUT)
net (div.out, ldo.FB)
net (div.lo, ldo.GND)
Common Traits:
- Reusable
pcb-module
definitions are typically parametric- Arguments are used to configure how those circuits behave - eg, what voltage to output.
- Some arguments might provide an
Instantiable
- meaning a component or module definition that will be instantiated inside this module.
- They will typically have standardized
port
interfaces using bundles. - They will typically use other lower level primitives to build up more complex features.
Example Component Wrappers
pcb-module MMA8451Q :
port bus : i2c
port rail : power
public inst comp : MMA8451Q-component
bypass-cap-strap(comp.VDD, comp.GND, 4.7e-6)
bypass-cap-strap(comp.BYP, comp.GND, 0.1e-6)
bypass-cap-strap(comp.VDDIO, comp.GND, 0.1e-6)
...
Common Traits:
- Module wrappers around components are typically useful to encapsulate component specific features.
- For example, specific bypass capacitor specs per the components datasheets.
- Configuration resistor strapping, which may require parametric arguments.
- Module wrappers are typically very specific to a particular component.
- It is common to make the wrapped component
public
as a way to interrogate it from the parent context.
- It is common to make the wrapped component
- These wrappers are often useful for using language features that are not present in the
pcb-component
definition context.
Statements
Here is the list of all of the statements you can use in a pcb-module
:
Statement | Description |
---|---|
#CHECK | Assertion Checks |
description | Description |
do-not-populate | Do not Populate |
eval-when | Eval When |
geom | Custom copper geometry |
insertion-loss | Add a Timing Difference Constraint - Topology |
inst | Instantiate a component or module |
instance-status | Instance Status |
layer | Layer |
layout-group | Layout Group |
name | Name |
net | Create an electrical connection |
no-connect | Set a port as "Not Connected" |
node | Node |
pin-properties | Pin Properties Table |
place | Set a component or module's pose |
ports | Ports |
reference-designator | Reference Designator Assignment |
require | Require - Pin Assignemnt |
restrict | Restrict - Pin Assignemnt |
ref-label | Ref Label Assignment |
schematic-group | Schematic Group |
short-trace | Short Trace |
structure | Structure - Topology |
supports | Supports - Pin Assignemnt |
symbol | Net Symbol Assignment |
timing | Add a Timing Constraint - Topology |
timing-difference | Add a Timing Difference Constraint - Topology |
topology-segment | Construct an SI route - Topology |
value-label | Value Label Assignment |
variant | BOM Variant Definitions |
Description
The description
statement is the optional descriptive field of a JITX object. Use it to store a description of the object for human designers reading JITX, and to make the object easier to find via text search. This description also shows up in the UI, such as the design explorer, to provide more insight into particular components and modules in the design.
Signature
description = <String|False>
This statement is optional. The default value will be false
in case no description
statement is found in a definition.
Any string is a valid description
value.
Each JITX definition may have exactly one description
statement. If more than one statement is encountered in a definition, then a DuplicateCStmtError
exception will be raised.
Examples
; We can use string literals to describe a particular component.
pcb-component analog-devices-ADM7150 :
description = "800 mA Ultralow Noise, High PSRR, RF Linear Regulator"
; We can use string formatting to construct descriptions based on variables
; or arguments to a JITX Definition
pcb-module band-pass-filter (high-cut:Double, low-cut:Double) :
description = to-string(
"Band-pass Filter - Highpass = %_ Hz and Lowpass = %_ Hz." % [high-cut, low-cut]
)
The eval-when
Statement
When constructing a component or module, there are often times checks we want to write that are dependent on the application. We need to know more about the other circuits we are connected to before we can write the check. To delay the evaluation of these checks until we have the information we need - we use the eval-when
statement.
The eval-when
statement is valid in the following contexts:
The
eval-when
statements work in tandem with the run-evals command. Therun-evals
function traverses the passed module and orchestrates the running of variouseval-when
blocks. This means foreval-when
statements in your modules/components to run - you must callrun-evals
at some point after you have completed your design declarations.
Signature
eval-when <CONDITION> :
<STATEMENT-1>
...
- The
<CONDITION>
is a predicate (ie, something that evaluates toTrue|False
). This condition indicates what data needs to be present in order for thiseval-when
to run. - The
<STATEMENT-1>
is a list of statements that are valid for the current context. This list of statements is typically called thebody
of theeval-when
statement. This can be general stanza code or any validpcb-component
orpcb-module
statements, depending on their respective contexts.
Usage
The eval-when
statement is a powerful method of customizing modules for the context in which those modules are instantiated. This method allows us to keep the logic that applies to a particular module or component co-located with the definition of that module or component.
pcb-component LDO :
pin-properties:
[pin:Ref | pads:Int ... ]
[VIN | 1 ]
[GND | 2 ]
[VOUT | 3 ]
...
property(self.VOUT.voltage) = 3.3
property(self.MAX_VIN) = 12.0
property(self.MAX_DROP_OUT) = 0.2
eval-when has-property?(self.VIN.voltage) :
val max-rating = property(self.MAX_VIN)
if self.VIN.voltage > max-rating:
fatal(
"Invalid Input Voltage - %_ exceeds max rating of %_" %
[self.VIN.voltage, max-rating]
)
val min-rating = property(self.VOUT.voltage) + property(self.MAX_DROP_OUT)
if self.VIN.voltage < min-rating :
fatal(
"Invalid Input Voltage - %_ is less than min rating of %_" %
[self.VIN.voltage, min-rating]
)
In this case, we're defining a simple, fixed-voltage LDO regulator. We want to check that the voltage applied to the VIN
port is within the expected and allowed range for this component.
For these checks to work - we need to know what voltage is being applied to the VIN
port and we don't necessarily know that when the component is instantiated. The eval-when
is waiting for the application of a voltage
property on the VIN
port. Some other entity is going to apply this at the application layer.
Once the voltage
property exists on VIN
, the statements in the body will execute. In this case we do some minimal checks on the accepted voltage range of VIN
.
With Great Power - Comes Great Responsibility
You might notice that this adds some conditional logic to what would otherwise be a purely declarative component or module. This is a concept that doesn't really exist in other legacy CAD tools - ie, you don't typically have to worry about the components mutating.
The safest operations to use inside an eval-when
clause are operations that don't modify the physical nature of the PCB:
- Checks - These are typically non-mutating and only read properties or structures.
- BOM Variations - Changing the BOM or any variants is usually OK.
- Adding
no-connect()
statements - Adding
property()
statements
Operations that are more difficult to use consistently in an eval-when
body include:
- Adding
inst
andnet
statements - Adding concrete ports with the
port
statement. - Adding new abstract ports with the
require
statement.
There are certainly cases where you might want to use these statements in an eval-when
body. These statements are supported and will execute as part of the design run. We suggest proceeding with care and purpose when using these statements.
Geometry
geom
is used to create custom copper geometry (copper shapes, pours, and vias) and associate it with a net.
Syntax
geom(mynet) :
via(LayerIndex(0), LayerIndex(1),
radius = 0.1, hole-radius = 0.05) at Point(0.0, 0.0)
copper(LayerIndex(1)) = Rectangle(5.0, 5.0)
Description
geom(mynet) :
Creates a geom
environment inside of which you can add copper
, copper-pour
, and via
statements and associate the geometry with mynet
.
Instance
inst
is how we add modules and components to modules.
Signature
inst <REF> : <COMPONENT>
inst <REF> : <MODULE>
inst <REF> : <COMPONENT>[10]
inst <REF> : <COMPONENT>[[1,3,5]]
Syntax
pcb-module my-module:
inst my-inst : my-component
inst my-inst-array : my-component[10]
for i in 0 to 10 do :
inst r : chip-resistor(1.0e3)
inst my-module-inst : my-other-module
Description
inst my-inst : my-component
Add an instance named my-inst
of type my-component
to this module.
inst my-inst-array : my-component[10]
Add an instance array named my-inst-array
of type my-component
to this module. This creates ten instances of my-component
with names my-inst-array[0]
through my-inst-array[9]
for i in 0 to 10 do :
inst r : chip-resistor(1.0e3)
Add a instance named r
of type chip-resistor(1.0e3)
inside a for
loop. If we needed to refer to r
to connect to its ports, we could only do so inside this loop.
inst my-module-inst : my-other-module
Modules can be instantiated just like components. We can nest modules as deeply as we like.
Layer
The layer
statement is used to create geometry on the non-copper layers of a circuit board. The layer()
statement is valid in the following contexts:
Signature
layer(<LayerSpecifier>) = <Shape>
<LayerSpecifier>
- A LayerSpecifier instance that identifies which non-copper layer to apply the provided geometry to.<Shape>
- AShape
instance that defines the geometry that will be created on the specified layer.
Usage
The most common usage of the layer()
statement is in pcb-landpattern:
pcb-landpattern diode-lp :
pad c : smd-pad(0.4, 0.75) at loc(-1.25, 0.0) on Top
pad a : smd-pad(0.4, 0.75) at loc(1.25, 0.0) on Top
layer(Silkscreen("body", Top)) = LineRectangle(1.8, 1.0)
layer(Silkscreen("body", Top)) = Line(0.1, [Point(-0.70, -0.5), Point(-0.70, 0.5)])
layer(Courtyard(Top)) = Rectangle(3.2, 2.0)
layer(ForbidCopper(LayerIndex(0))) = Rectangle(2.0, 1.0)
This will construct a landpattern that looks like this:
Notice the silkscreen in yellow with cathode marker. The blue box is the ForbidCopper
layer on the Top Layer. Red is the top copper pads for the cathode c
and anode a
.
The white bounding rectangle is the Courtyard
layer.
See LayerSpecifier for more information about specific layers.
Cutouts
When constructing cutouts in the board layout, your best bet is to use a solid region as opposed to a line. A line can confuse the routing engine into thinking that there are two physically separate regions where copper can be placed.
Consider a USB connector part, U231-096N-3BLRT06-SS, Jing Extension of the Electronic Co.
Here is an excerpt from the datasheet:
If we draw the cutout with a line, as shown in the datasheet, we get this:
pcb-landpattern USB-conn:
...
layer(Cutout()) = Line(0.254, [Point(-7.300, -7.650), Point(-8.450, -7.650)])
layer(Cutout()) = Polyline(0.254, [
Point(6.850, 4.650)
Point(6.850, -7.650)
Point(8.450, -7.650)])
layer(Cutout()) = Polyline(0.254, [
Point(-6.850, 4.650)
Point(-6.850, -7.650)
Point(-7.300, -7.650)])
layer(Cutout()) = Line(0.254, [Point(-6.850, 4.650), Point(6.850, 4.650)])
The cutout line is in gold color. Notice that the ground layer (blue) copper is present on both sides of the cut line with some margin in between. The routing engine basically thinks that the cutout is just the line. If we were making a slot - that would probably be reasonable. But for this case, we want the hole region between the cutout line and the board edge (red) to be a cutout region.
The right way is to use a Rectangle
or Polygon
solid geometry:
pcb-landpattern USB-conn :
...
layer(Cutout()) = Polygon([
Point(-6.850, 4.650), Point(6.850, 4.650),
Point(6.850, -7.650), Point(-6.850, -7.65),
])
layer(Cutout()) = Circle(Point(-6.85 + 0.5, 4.65), 0.5)
layer(Cutout()) = Circle(Point(6.85 - 0.5, 4.65), 0.5)
Notice that the cutout region fills the entire connector region and the blue ground plane is not present in the cutout region.
Instance Status
instance-status
and do-not-populate
are statements used to modify the on-board and BOM statuses of components.
Signature
instance-status(<INST>):
bom-status = <ComponentBOMStatus>
board-status = <ComponentBoardStatus>
do-not-populate(<INST>)
A ComponentBOMStatus
is an enum of:
InBOM
NotInBOM
MarkedDNP
InBOMVariant
A ComponentBoardStatus
is an enum of:
OnBoard
NotOnBoard
InBoardVariant
In this context <INST>
is a ref to a instantiated component or module in this module's context.
Syntax
pcb-module my-module:
inst fuse : components/manu/broken-fuse
inst jumper-a : components/manu/normal-jumper
inst jumper-b : components/manu/normal-jumper
inst jumper-c : components/manu/normal-jumper
inst dnp-me : components/manu/normal-jumper
instance-status(fuse) :
bom-status = NotInBOM
instance-status(jumper-a) :
board-status = NotOnBoard
instance-status(jumper-b) :
bom-status = MarkedDNP
board-status = OnBoard
do-not-populate(dnp-me)
Description
instance-status(instance-name) :
Tells which component inst is affected by the sub-statement(s) below.
Possible sub-statements:
bom-status = InBOM|NotInBOM|MarkedDNP|InBOMVariant
.InBOM
is the default whenbom-status
is missing.board-status = OnBoard|NotOnBoard|InBoardVariant
.OnBoardBOM
is the default whenboard-status
is missing.
do-not-populate(instance-name)
Mark a component instance as a DNP component. This is equivalent to bom-status = MarkedDNP
using instance-status(instance-name)
.
do-not-populate
always supersedes instance-status
no matter where they are in the code.
In this example,
fuse
is [NotInBOM, OnBoard]
jumper-a
is [InBOM NotOnBoard]
, jumper-b
is [MarkedDNP OnBoard]
, and jumper-c
is [InBOM OnBoard]
.
dnp-me
is [MarkedDNP OnBoard]
Related JITx Commands
There are 2 commands to query the component status of a component instance.
do-not-populate?(instance-name)
and instance-status?(instance-name)
do-not-populate?(instance-name)
will return True|False
.
instance-status?(instance-name)
will return a Tuple
of ComponentBOMStatus
and ComponentBoardStatus
.
for example, [InBOM NotOnBoard]
Insertion Loss Constraint
The insertion-loss
statement is used to apply an insertion loss constraint on a
signal topology. This constraint is used by the routing engine when routing a signal
on the board.
This statement is only valid within the pcb-module
context.
Signature
; Key-Value Endpoint Variant
insertion-loss(<REF>) = <InsertionLossConstraint>
; Explicit Endpoint Variant
insertion-loss(<REF-1>, <REF-2>) = <InsertionLossConstraint>
The insertion-loss
statement expects two endpoints. Each endpoint is
a ref to a SinglePin
port of a component or module instance. Each of these
ports must be part of a valid topology.
In most applications, the endpoints are the extrema of a signal topology.
The Constraint
An instance of InsertionLossConstraint
is applied to the insertion-loss
statement.
defstruct InsertionLossConstraint <: SignalConstraint :
min-loss:Double
max-loss:Double
defn InsertionLossConstraint (loss:Toleranced) -> InsertionLossConstraint:
...
The min-loss
and max-loss
are relative attenuation limits in units of dB. max-loss
must be greater than min-loss
.
This insertion loss features of the signal's route are determined by the DifferentialRoutingStructure
as applied via the structure statement.
Usage
pcb-differential-routing-structure diff-90:
...
pcb-bundle usb2-data:
data : diff-pair
pcb-component PIC16F1455:
port bus : usb2-data
...
pcb-component USBMicroB:
port bus : usb2-data
...
pcb-module usb2:
inst MCU : PIC16F1455
inst conn : USBMicroB
; Construct the Topology - Note that these statements
; can operate on the bundle types (in this case `diff-pair`)
net (MCU.bus.data, conn.bus.data)
topology-segment(MCU.bus.data, conn.bus.data)
; Insertion Loss constraints require a routing structure application
; to know how to compute the loss metric for each layer and route
; type in a design. See the `structure` statement docs for more info.
structure(...) = diff-90
val max-loss = InsertionLossConstraint(
min-max(0.0, 12.0)
)
; Insertion Loss Constraints are applied on SinglePin ports
; so this requires two statements for a `diff-pair` bundle.
insertion-loss(MCU.bus.data.P => conn.bus.data.P) = max-loss
insertion-loss(MCU.bus.data.N => conn.bus.data.N) = max-loss
; Equivalent Representation
; insertion-loss(MCU.bus.data.P, conn.bus.data.P) = max-loss
; insertion-loss(MCU.bus.data.N, conn.bus.data.N) = max-loss
In this example, the insertion loss is applied to the extrema of
each of the P
and N
signals of the diff-pair
bundle.
Notice that the insertion-loss
statement applies to SinglePin
objects. The JSL library provides tools for applying these primitives
to more complicated Bundle
and PortArray
structures.
Instance Status
instance-status
and do-not-populate
are statements used to modify the on-board and BOM statuses of components.
Signature
instance-status(<INST>):
bom-status = <ComponentBOMStatus>
board-status = <ComponentBoardStatus>
do-not-populate(<INST>)
A ComponentBOMStatus
is an enum of:
InBOM
NotInBOM
MarkedDNP
InBOMVariant
A ComponentBoardStatus
is an enum of:
OnBoard
NotOnBoard
InBoardVariant
In this context <INST>
is a ref to a instantiated component or module in this module's context.
Syntax
pcb-module my-module:
inst fuse : components/manu/broken-fuse
inst jumper-a : components/manu/normal-jumper
inst jumper-b : components/manu/normal-jumper
inst jumper-c : components/manu/normal-jumper
inst dnp-me : components/manu/normal-jumper
instance-status(fuse) :
bom-status = NotInBOM
instance-status(jumper-a) :
board-status = NotOnBoard
instance-status(jumper-b) :
bom-status = MarkedDNP
board-status = OnBoard
do-not-populate(dnp-me)
Description
instance-status(instance-name) :
Tells which component inst is affected by the sub-statement(s) below.
Possible sub-statements:
bom-status = InBOM|NotInBOM|MarkedDNP|InBOMVariant
.InBOM
is the default whenbom-status
is missing.board-status = OnBoard|NotOnBoard|InBoardVariant
.OnBoardBOM
is the default whenboard-status
is missing.
do-not-populate(instance-name)
Mark a component instance as a DNP component. This is equivalent to bom-status = MarkedDNP
using instance-status(instance-name)
.
do-not-populate
always supersedes instance-status
no matter where they are in the code.
In this example,
fuse
is [NotInBOM, OnBoard]
jumper-a
is [InBOM NotOnBoard]
, jumper-b
is [MarkedDNP OnBoard]
, and jumper-c
is [InBOM OnBoard]
.
dnp-me
is [MarkedDNP OnBoard]
Related JITx Commands
There are 2 commands to query the component status of a component instance.
do-not-populate?(instance-name)
and instance-status?(instance-name)
do-not-populate?(instance-name)
will return True|False
.
instance-status?(instance-name)
will return a Tuple
of ComponentBOMStatus
and ComponentBoardStatus
.
for example, [InBOM NotOnBoard]
Layout Group
layout-group
is used to control the placer in JITX, which arranges our components automatically on our board. The placer will try to place components in a layout group close to each other (with a controllable weighting), and optionally will arrange the layout groups off to the side of the board to make manual placement easier.
Syntax
pcb-module reg
layout-group(self) = reg
pcb-module my-module
layout-group(my-inst) = power
layout-group(my-other-inst) = test
layout-group([test-points measure]) = test
layout-group(load) = layout-group(my-inst)
Description
pcb-module reg :
layout-group(self) = reg
This statement will apply a layout group to all of the components that are instantiated inside the module reg
. Multiple instances of reg
will be grouped individually.
layout-group(my-inst) = power
Set the layout-group
of my-inst
to be power.
layout-group(my-other-inst) = test
Set the layout-group
of my-other-inst
to be test.
layout-group([test-points measure]) = test
Set the layout-group
of test-points
and measure
to also be test.
layout-group(load) = layout-group(my-inst)
Set the layout-group
of load
to be the same as the layout-group
of my-inst
.
Name
The name
statement is the optional name field of a JITX object. Use it to store a descriptive name as a String
. This name will often be used in the UI in place of the object's expression name for better readability.
Signature
name = <String>
This statement is optional. The default value will be the definition's symbol name in case no name
statement is found in a definition.
Any string is a valid name
value.
Usage
Literal String Names
pcb-component component :
name = "ADM7150"
pcb-module band-pass-filter :
name = "Band-pass filter"
The examples name = "ADM7150"
and name = "Band-pass filter"
use a String
literal for the name.
Formatted Strings
pcb-pad smd-pad (anchor:Anchor, w:Double, h:Double) :
name = to-string("%_x%_ %_ SMD Pad" % [w,h,anchor])
pcb-landpattern test-lp:
pad p[1] : smd-pad(C, 0.6, 0.7) at loc(x0,y0)
We can also construct strings using formatting routes and parameter arguments for a particular definition. In this example, the constructed smd-pad
name property would be 0.6x0.7 C SMD Pad
Net
The net
statement defines an electrical connection between ports of components and modules. With the net
statement we can connect single pins, bundles, and port arrays, as long as the types match. Optionally, nets can be assigned names.
The net
statement is only valid inside pcb-module
contexts.
Signature
The net
statement has two basic forms:
- Forward Declaration - This form is used to declare a net by name before using it in a circuit.
- Instantiation - This form is used to construct a net or add ports to an existing net directly in a given statement.
; Forward Declaration
net <NAME>
net <NAME> : <TYPE>
; Instantiation
net (<REF-1>, <REF-2>, ...)
net <NAME> (<REF-1>, <REF-2>, ...)
net <NAME> : <TYPE> (<REF-1>, <REF-2>, ...)
<NAME>
- Symbol name for this net that is unique in the current module context. This<NAME>
parameter is optional in the instantiation form. If not present, then this is considered an anonymous net.<TYPE>
- An optional port type that allows the construction of arbitrary net port types. Typically this type would be apcb-bundle
type(<REF-1>, ...)
- The list of refs provides the identification of which pins or other nets to form connections with in the instantiation form.
Public Accessibility
By default, all net
statements are private. This means that the connection created by the net
statement is not accessible from outside the pcb-module
context what it was written. A public
prefix modifier can be added to a named net
statement to allow access by external entities.
Example
pcb-module transceiver:
...
public net OSC (mixer.osc-in, crystal.p[1])
pcb-module top-level:
inst U1 : transceiver
inst debug : connector
net (U1.OSC, debug.p[1])
In this example the net OSC
in the transceiver
module is marked public
which means it is accessible from the parent context top-level
. If OSC
had not been marked public
, then the JITX runtime would have issued a Cannot access named field 'U1.OSC'.
error.
Usage
Single Pin Nets
pcb-module v-div:
port vin
port vout
port gnd
inst R1 : chip-resistor(10.0e3)
inst R2 : chip-resistor(10.0e3)
; Anonymous Net Construction
net (vin, R1.p[1])
net (R1.p[2], R2.p[1], vout)
net (R2.p[2], gnd)
This is the most common usage pattern for net
statements in a circuit. All of the nets are anonymous (ie, they have no specific given name). These nets will each be assigned a name by the JITX runtime as the circuit design is elaborated.
Named net
in net
We can use named nets as an element of the reference list in a subsequent net
statement.
pcb-module multiple-sensors:
; Forward Declaration of the Ground Net
net GND
inst U1 : Accelerometer
net (U1.gnd, GND)
inst U2 : Temperature-Sensor
; This is legal
net OTHER-NAME (U2.gnd, GND)
In this example, we forward declare the GND
net for our circuit so that we can use it connect the ground connection of our various sensor components.
Notice that you can create additional net reference names (ie, OTHER-NAME
in this example) that are aliases to the GND
net.
Net on Bundles / Arrays
The net
statement also works on Bundle
and PortArray
types.
pcb-bundle SPI:
port poci
port pico
port sclk
pcb-component sd-card:
port bus : SPI
...
pcb-component mcu:
port bus : SPI
...
pcb-module top-level:
inst host : mcu
inst dev : sd-card
net (host.bus, dev.bus)
In this example, the two components host
and dev
both export ports of type SPI
. The net
statement in this case constructs 3 distinct connections:
host.bus.poci
<=>dev.bus.poci
host.bus.pico
<=>dev.bus.pico
host.bus.sclk
<=>dev.bus.sclk
Attempting to net
two ports of different types (ie, a pin to a bundle, or an i2c
bundle to an SPI
bundle) will result in an error.
PortArray
types behave similarly where a net
statement makes an element-wise 1:1 connection between the pins of the PortArray
ref instances.
Arrays of Nets
With forward declaration, we can construct arrays of nets for usage in a for
loop or other sequence:
pcb-bundle MIPI-CSI:
port clk : diff-pair
port data : diff-pair
pcb-module my-module:
val num-cams = 16
inst host : mcu
inst cams : camera[ num-cams ]
net bus:MIPI-CSI[ num-cams ]
for i in 0 to num-cams do:
net (bus[i], host.MIPI[i], cams[i].MIPI)
In this example, we forward declare an array of MIPI busses. We then use a for
loop to make num-cams
connections between the host
and the each camera.
In this example we could have skipped the forward declaration of the net and used the following and accomplished the same connections:
net (host.MIPI[i], cams[i].MIPI)
The short comings of this approach are:
- If we wanted to set a name for these nets, then this becomes difficult
- First, We would need to construct a string name for the net
- Example:
val name = to-string("bus-%_" % [i])
- Example:
- Then we would need to apply this name some how to the net construction.
- The easiest way right now is to use the make-net function
make-net(name, [host.MIPI[i], cams[i].MIPI])
- First, We would need to construct a string name for the net
- Unless we somehow save the string
name
that we constructed above, then there is really no way to reference that net at a later time.
By forward declaring the net, we avoid these difficulties.
No Connect Statement
The no-connect()
statement is a tool for marking a port on a component or module as "Not Connected". This is an indication to the JITX runtime that this port can be left floating without any ill-effect.
This statement can be used from the following contexts:
Signature
no-connect(<PORT>)
<PORT>
- The argument to this statement is expected to be aSinglePin
port from apcb-component
definition or instance.
Usage
The no-connect()
statement is typically used to mark individual pins as intentionally not connected:
public pcb-component component :
name = "XDCR_LIS3DHTR"
manufacturer = "STMicroelectronics"
mpn = "LIS3DHTR"
pin-properties :
[pin:Ref | pads:Int ... | side:Dir]
[CS | 8 | Left]
[GND | 5, 12 | Down]
...
[nc[0] | 2 | Down]
[nc[1] | 3 | Down]
[VDD-IO | 1 | Up]
make-box-symbol()
assign-landpattern(xdcr-lis3dhtr)
for i in indices(self.nc) do:
no-connect(self.nc[i])
Here the LIS3DHTR
has two "NC" pins, pin 2 and 3. This component defines these pins in a PortArray
of length 2. The for-loop at the end uses the indices command to loop over all the NC pins.
Notice - that we did not pass self.nc
, the PortArray
, directly to the no-connect
statement. This would elicit an error.
When we view this component in the schematic, we see:
Notice the X
over the two NC pins. This is the "No Connect" representation in the schematic.
Usage from pcb-module
When using this statement from a module, we must use the no-connect()
statement on one of the ports of the instantiated components in the module. It is an error to apply the no-connect()
statement to one of a module's ports.
public pcb-module module :
public inst acc : ocdb/components/st-microelectronics/LIS3DH/component
...
for i in indices(acc.nc) do:
no-connect(acc.nc[i])
Note that duplicate no-connect()
statements on a component's port will not throw an error.
If you attempt to use the no-connect
statement on a module's port:
pcb-module bad-module:
port unused : pin
no-connect(self.unused)
This will throw an exception:
Uncaught Exception: Tried to get type with an invalid definition.
Testing for "No Connect" Status
The no-connect? command can be used to interrogate a component and determine the "No Connect" status of its pins.
Node
The node
statement creates an artificial electrical attachment point.
The node
statement is only valid from within a pcb-module
context.
Signature
; Single Node Instance
node <NAME>:<TYPE>
; Array Node Instance
node <NAME>:<TYPE>[<ARRAY:Int|Tuple>]
<NAME>
- Symbol name for the node in this context. This name must be unique in the current context.<TYPE>
- The type of port to construct. This can be the keywordpin
for a single pin type or any declaredpcb-bundle
type.<ARRAY:Int|Tuple>
- Optional array initializer argument. This value can be:Int
-PortArray
type constructed with lengthARRAY
. This array is constructed as a contiguous zero-index array of nodes.Tuple
-PortArray
constructed with an explicit set of indices. This array is not guaranteed to be contiguous.
Watch Out! - There is no space between
<TYPE>
and the[
opening bracket of the array initializer.
Usage
The node
is often a tool for creating intermediate connection points in a circuit. You will see this functionality used for:
- Fanout - Given a port on a component instance, a node array can fanout that signal to multiple connection points.
- Return Values - When writing generators using the
inside pcb-module:
syntax, it is often useful to be able to return a port-like object.
The utility of a node
statement is often abstract and doesn't have a physical component associated with it.
Example - Reverse Differential Pair
defn reverse-pair (p:JITXObject) :
inside pcb-module:
node temp:diff-pair
topo-net(p.P, temp.N)
topo-net(p.N, temp.P)
temp
This function assumes that the argument p
is a diff-pair
port. This generator function constructs a node and then swaps the P
and N
ports of the input diff-pair before assignment.
Notice that this function is using topo-net but that isn't a requirement. A simple net
statement would also work if signal integrity constraints aren't required.
Pin Properties
The pin-properties
statement defines a table for quickly and easily constructing the ports for a component. In addition to defining the ports, the pin-properties
table can also easily add property
statements to each pin in the table. This statement is effectively an automation tool for more easily generating lower level JITX statements.
The pin-properties
statement is valid in the pcb-component
and pcb-module
contexts. Each component or module may contain one or more pin-properties
tables.
While you can use pin-properties
in the pcb-module
context, it is most typically found
in the pcb-component
context.
Signature
pin-properties:
<HEADER>
<ROW-1>
<ROW-2>
...
<HEADER>
- Defines the schema for each following rows of the pin properties table.<ROW-*>
- Each row describes aSinglePin
port of this component.
<HEADER> Signature
[ <NAME-1>:<TYPE-1> | <NAME-2>:<TYPE-2> | ... ]
The <HEADER>
consists of a fixed-length set of <NAME>:<TYPE>
pairs. The <NAME>
value defines the name of the property for this table. Commonly used property names include:
pin
- This identifies whichSinglePin
port of the component that the following properties apply to. This is typically aRef
type.pads
- This identifies which pads on the land pattern map to a particular. This is typically used with eitherInt
orRef
type and is often combined with the...
operator. See more information below.bank
- This identifier is used withInt
orRef
type to identify the banks of a multi-part symbol.side
- This identifier is commonly used with the "Box Symbol" generators. This property is used with theDir
type to indicate which side of the box a pin will be placed on.
Beyond these properties, any identifier and type can be used to add custom properties to ports.
The ...
Operator
The ellipsis points operator ...
is used in the header to indicate that this column may contain multiple values. This is typically used for the pads
identifier to map multiple land pattern pads to a specified port.
<ROW-*> Signature
[ <VALUE1> | <VALUE2> | ... ]
Each row contains the properties to assign to a particular component SinglePin
port. The row's pin
property is typically the first column and identifies which port the following properties will apply to. The pin
property typically also matches the schematic symbol's pin refs but that isn't a strict requirement. This is only convenient when using the assign-symbol
or assign-symbols
commands which handle mappings automatically if the names are consistent.
The referenced pin must be a SinglePin
- it can't be an array or a bundle port. This is important because we need to be able to apply properties like the pads
to a particular single pin.
Each row must contain the same number of columns as the header. Otherwise - an exception will be thrown.
Example Pin Properties Table
pcb-component EEPROM-24AA025:
...
pin-properties :
[pin:Ref | pads:Ref ... ]
[SDA | p[3] ]
[SCL | p[1] ]
[VCC | p[6] ]
[VSS | p[2] ]
[A0 | p[5] ]
[A1 | p[4] ]
This table is an example from the Microchip, 24AA025 EEPROM chip.
In this particular example - there is a 1:1 mapping between the schematic symbol pins and the pads of the landpattern. Both the pin
and the pads
are of type Ref
. For the pin
properties, this means that each name SDA
, SCL
, etc are defined as SinglePin
ports on this component. For the pads
, we are referencing the conventional p
array that defines the pins of a landpattern. This means that p[1]
corresponds with pin 1, p[2]
corresponds with pin 2, etc:
This table could have been defined identically as:
pcb-component EEPROM-24AA025:
...
pin-properties :
[pin:Ref | pads:Int ... ]
[SDA | 3 ]
[SCL | 1 ]
[VCC | 6 ]
[VSS | 2 ]
[A0 | 5 ]
[A1 | 4 ]
Notice that in this case pads
is of type Int
and we don't use the p[*]
syntax. We reference the integer pin directly. This is a short hand for the p[*]
syntax.
For this simple example - it may not be obvious why you might use a Ref
instead of a Int
. Typically, we see Ref
used for large BGA packages where you will see K[1]
, N[5]
, AB[2]
, or similar references from a 2D grid.
N:1 Pads to Ports
There are some cases where you may want to assign multiple land pattern pads to a single port. This is very common for ground and power pins in large packages.
pcb-component FPGA:
...
pin-properties :
[pin:Ref | pads:Ref ... ]
[ VCCINT | A[2] B[10] C[3] C[4] ]
[ GND | A[3] A[7] B[2] B[8] ]
...
In this example, we construct a component with multiple pads devoted to the VCCINT
and GND
pins.
Notice how each of the pins has multiple pads associated with it. The engine attempts to shorten the pad references shown next to the pin as much as it can. In this case, it means that for each C
pad referenced in VCCINT
, we use a ,
(comma) delimited list of indices. Similarly in GND
, both the A
and B
pads have 2 indices listed.
This style of N:1
pad to pin referencing can be useful but if the value of N
grows large, this can become unwieldy. Another way to structure this would be to use unique pins for each pad (ie, strictly 1:1
mapping):
pcb-component FPGA:
...
pin-properties :
[pin:Ref | pads:Ref ... ]
[ VCCINT[0] | A[2] ]
[ VCCINT[1] | B[10] ]
[ VCCINT[2] | C[3] ]
[ VCCINT[3] | C[4] ]
[GND[0] | A[3] ]
[GND[1] | A[7] ]
[GND[2] | B[2] ]
[GND[3] | B[8] ]
...
In this variant, each of the VCCINT
and GND
pins has a 1:1
mapping with a pad. This can be useful for the case where there may be 10's or 100's of these pins. This trades more schematic pixel space for a more readable pin to pad mapping.
Individually listing each pins in a table like this is going to become a bit tedious though. Fortunately, we're not just entering data, we can write a bit of code too:
val VCCINT-PADS = [
Ref("A")[2],
Ref("B")[10],
Ref("C")[3],
Ref("C")[4]
]
pin-properties :
[pin:Ref | pads:Ref ... ]
for (rf in VCCINT-PADS, i in 0 to false) do:
[ VCCINT[i] | (rf) ]
The VCCINT-PADS
tuple in this example is a bit contrived. The more likely source of the pad information for a component would be a file like the Xilinx Pinout Package files. The important part is the use of the for
loop to construct each of the rows of the pin-properties
table.
Note the use of the ()
around the rf
value. These parenthesis are necessary to convert the value into a Ref
symbol.
Assigning Properties for the Box Symbol
The pin
and pads
properties are not the only features that we can add through the pin-properties
table.
The header of the table is customizable and allows any number of properties to be assigned to each pin
as needed.
Here is an example where we assign a side
property of type Dir
to each pin. These types of properties
are very useful for when working with the "BoxSymbol" utilities:
pcb-component mycomponent :
mpn = "DAC53001"
pin-properties :
[pin:Ref | pads:Int ... | side:Dir ]
[VDD | 15 | Right ]
[VREF | 16 | Right ]
[OUT0 | 2 | Right ]
[FB0 | 1 | Right ]
[CAP | 13 | Right ]
[SDO | 5 | Left ]
[SYNC# | 6 | Left ]
[SDI | 7 | Left ]
[SCLK | 8 | Left ]
[NC | 3, 4, 9, 10, 11, 12 | Left ]
[AGND | 14, 17 | Left ]
val box = BoxSymbol(self)
set-alignment(N, self.VDD, self.VREF)
set-alignment(S, self.AGND, self.NC, self.CAP)
set-head-margin(1.0, self.NC)
set-head-margin(1.0, self.OUT0, self.FB0)
val symb = create-symbol(box)
assign-symbol(symb)
Here is an example rendering of this component:
Notice how the pins of the symbol get assigned to one side or the other depending on
the side
property. Notice also that the order of the pins in the box symbol depends on
the ordering in the table. Some of the details regarding the formatting are left out in this
example.
Another point of interest is that the NC
and AGND
are multi-pad pins in this symbol. Notice
how for those pins, the pad identifier for the pin has multiple pad references in it.
This symbol was created with the BoxSymbol from JSL. See JSL for more information about how to set alignment, margin, and other properties.
Assigning the Bank via the Table
The bank
property is used to construct multi-part symbols. We can assign the bank association in the pin-properties
table. The bank
property is of type Int|Ref
meaning that it can either be
an Int
value or a Ref
value. When building a part, you will typically use all Int
or all Ref
for the banks
in a component.
Here is an excerpt of a component that uses the bank
property as a Ref
in the pin properties table:
public pcb-component USBMux :
manufacturer = "Texas Instruments"
mpn = "HD3SS3220RNHR"
reference-prefix = "U"
port TX-O : diff-pair
port RX-O : diff-pair
port TX : diff-pair[[1, 2]]
port RX : diff-pair[[1, 2]]
pin-properties :
[pin:Ref | pads:Ref ... | side:Dir | bank:Ref]
[VBUS_DET | p[5] | Left | control]
[ID | p[27] | Left | control]
[CC2 | p[1] | Left | control]
[CC1 | p[2] | Left | control]
[CURRENT_MODE | p[3] | Left | control]
[PORT | p[4] | Left | control]
[ENn_CC | p[29] | Left | control]
[TX-O.P | p[6] | Right | mux]
[TX-O.N | p[7] | Right | mux]
[RX-O.P | p[9] | Right | mux]
[RX-O.N | p[10] | Right | mux]
...
This results in two symbol parts for the component:
Multiple Pin Property Tables
When defining pin-properties
tables, the number of rows typically equals the number of pins in the component we are attempting to model. With a large number of pins, this amount of data can be quickly become overwhelming. Adding or removing columns from the table becomes a chore.
Because of this it is common to keep the tables focused on one particular application at a time. For example, the primary application is creating the pin to pad mapping. That is what the first (and usually only) pin-properties
table will do.
Once you have defined this mapping, you can either add columns to this table to introduce other properties, or you create an entirely new table. If we refer back to the 24AA025
example from earlier, we could add a second table in our component definition like this:
pcb-component EEPROM-24AA025:
...
pin-properties :
[pin:Ref | pads:Int ... ]
[SDA | 3 ]
[SCL | 1 ]
[VCC | 6 ]
[VSS | 2 ]
[A0 | 5 ]
[A1 | 4 ]
pin-properties:
[pin:Ref | side:Dir ]
[SDA | Left ]
[SCL | Left ]
[VCC | Right ]
[VSS | Left ]
[A0 | Right ]
[A1 | Right ]
...
This example is a trivial case, but you could imagine adding additional custom properties or other data.
The important things to consider are:
- The
pin:Ref
column is like the "Primary Key" of this table. Everypin-properties
table instance will need to reference thepin:Ref
property as the first column. - The first
pin-properties
table defines the ports of the component. Thepin:Ref
properties in subsequent tables must match with thepin:Ref
properties in the first table.- The subsequent tables can have a sub-set of the
pin:Ref
properties from the first table. - No new, unique
pin:Ref
properties can be defined in subsequent tables.
- The subsequent tables can have a sub-set of the
For example - This is OK:
pin-properties :
[pin:Ref | pads:Int ... ]
[SDA | 3 ]
[SCL | 1 ]
[VCC | 6 ]
[VSS | 2 ]
[A0 | 5 ]
[A1 | 4 ]
; OK - properties only defined on these pins.
pin-properties:
[pin:Ref | card:Cardinality ]
[SDA | Bidir ]
[SCL | Bidir ]
[A0 | Input ]
[A1 | Input ]
We don't define any new pins - we just don't include the VCC
and VSS
in the second table. This means that SDA
, SCL
, A0
, and A1
will all have a card
property but VCC
and VSS
will not.
But the following is NOT OK:
pin-properties :
[pin:Ref | pads:Int ... ]
[SDA | 3 ]
[SCL | 1 ]
[VCC | 6 ]
[VSS | 2 ]
[A0 | 5 ]
[A1 | 4 ]
; BAD - Will throw an error
pin-properties:
[pin:Ref | pads:Int ... ]
[NC | 7 ]
[NC | 8 ]
This will result in the runtime throwing an exception.
Ignored Values on Properties
Sometimes you have a table structure and you don't want to set a property on a particular pin. The -
special value is here to the rescue. This value basically means "Don't set this property".
pin-properties :
[pin:Ref | pads:Int ... ]
[SDA | 3 ]
[SCL | 1 ]
[VCC | 6 ]
[VSS | 2 ]
[A0 | 5 ]
[A1 | 4 ]
; OK - properties only defined on these pins.
pin-properties:
[pin:Ref | card:Cardinality | i2c-bus:(True|False) ]
[SDA | Bidir | true ]
[SCL | Bidir | true ]
[A0 | Input | - ]
[A1 | Input | - ]
Notes:
- Notice that in the
i2c-bus:(True|False)
property type, there is a set of()
around theTrue|False
type. This is necessary for all Union Types inpin-properties
headers. Otherwise, you will get a syntax error. - The
-
special value basically means thatA0
andA1
in this example will not have ai2c-bus
property. Thehas-property?(self.A0.i2c-bus)
function will returnfalse
.
Place - how to place a instance or module in JITX
In JITX, we can programmatically place instances or modules on our board with a single line of code.
First, we specify place
and the instance that we want to place and then we specify the location. Finally, we specify if we want to place it on the top or the bottom of the board.
Example placement
inst c : my-component
place(c) at loc(2.0, 3.0) on Top
loc
The loc
statement is for defining a location for placement in JITX. It creates an object describing translation and rotations that can be used to create geometric constraints.
loc
statements can be multiplied together (i.e. the transforms can be composed) and used to create trees of kinematic constraints.
pcb-landpatterns
and pcb-modules
have a right-handed coordinate system with origin at loc(0.0, 0.0, 0.0)
. Items you place in that coordinate frame will move together. e.g. you can move a landpattern and all the pads stay in the same relative positions. You can place instances in a module, and the relative positions of those instances will remain fixed when you place the module.
Syntax
loc(1.0, 5.0)
loc(2.0, 3.0, 90.0)
loc(0.0, (- 3.0), (- 90.0)) on Bottom
loc(2.0, 3.0) on Top (relative-to proc)
inside pcb-landpattern :
pad p[0] : smd-pad(2.0, 1.0) at loc(0.0, 0.0, 90.0) * loc(2.0, 3.0)
pad p[1] : smd-pad(5.0, 1.0) at loc(2.0, 3.0) * loc(0.0, 0.0, 90.0)
inside pcb-module :
inst c : my-component
place(c) at loc(2.0, 3.0) on Top
Description
loc(1.0, 5.0)
Create a pose of (x,y) = (1.0mm, 5.0mm)loc(2.0, 3.0, 90.0)
Create a pose of (x,y, theta) = (1.0mm, 5.0mm, 90 degrees)loc(2.0, 3.0, (- 90.0)) on Top
Create a pose of (x,y, theta) = (1.0mm, 5.0mm, -90 degrees), specifying that the component is on Top of the board.loc(0.0, (- 3.0), (- 90.0)) on Bottom
Create a pose of (x,y, theta) = (0.0mm, -3.0mm, -90 degrees), specifying that the component is on the Bottom of the board.loc(2.0, 3.0) on Top (relative-to proc)
Create a pose of (x,y) = (2.0mm, 3.0), defined from the origin of an instance named 'proc'. This is a relative constraint.
pad p[0] : smd-pad(2.0, 1.0) at loc(0.0, 0.0, 90.0) * loc(2.0, 3.0)
pad p[1] : smd-pad(5.0, 1.0) at loc(2.0, 3.0) * loc(0.0, 0.0, 90.0)
These statements create and place two pads at locations found by composing several poses together. The ordering of poses matters when you are composing them.
The pose loc(0.0, 0.0, 90.0) * loc(2.0, 3.0)
first rotates the coordinate system by 90 degrees, and then translates it by (x,y) = (2.0mm, 3.0mm) in the new frame, so that the final pose is (x,y,theta) = (-3.0, 2.0, 90.0)
The pose loc(2.0, 3.0) * loc(0.0, 0.0, 90.0)
first translates by (x,y) = (2.0mm, 3.0mm), then rotates the coordinate system by 90 degrees, so that the final pose is (x,y,theta) = (2.0, 3.0, 90.0). Here is the resulting pad geometry (origin of the land pattern is the blue cross):
inst c : my-component
place(c) at loc(2.0, 3.0) on Top
Ports
port
is a JITX statement that defines the electrical connection points for a component or module.
The port statement can be used in the following contexts:
Each port
statement in any of these contexts is public
by default. This means that each port can be accessed externally by using dot
notation.
Signature
; Single Port Instance
port <NAME> : <TYPE>
; Array Port Instance
port <NAME> : <TYPE>[<ARRAY:Int|Tuple>]
<NAME>
- Symbol name for the port in this context. This name must be unique in the current context.<TYPE>
- The type of port to construct. This can be the keywordpin
for a single pin type or any declaredpcb-bundle
type.<ARRAY:Int|Tuple>
- Optional array initializer argument. This value can be:Int
-PortArray
constructed with lengthARRAY
. This array is constructed as a contiguous zero-index array.Tuple
-PortArray
constructed with an explicit set of indices. This array is not guaranteed to be contiguous.
Watch Out! - There is no space between
<TYPE>
and the[
opening bracket of the array initializer.
Syntax
There are multiple forms of the port
statement to allow for flexible construction of the interface to a particular component. The following sections outline these structures.
Basic Port Declaration
The user has two options when declaring a single pin:
; Single Pin 'a'
port a : pin
; Equivalent to
port a
The port a
structure is a shorthand version for declaring single pins.
Pin Arrays
To construct a bus of single pins, we use the array constructor:
port contiguous-bus : pin[6]
port non-contiguous-bus : pin[ [2, 5, 6] ]
println(indices(non-contiguous-bus))
; Prints:
; [2 5 6]
The contiguous-bus
port is defined as a contiguous array of pins with indices: 0, 1, 2, 3, 4, & 5.
The non-contiguous-bus
port is defined an array with explicit indices: 2, 5, & 6. The indices
function is a helper function for determining what array indices are available on this port.
Attempting to access a port index that does not exist or is negative will result in an error.
Bundle Port
A bundle port is a connection point that consolidates multiple individual signals into one entity. The structure of this port is defined by the pcb-bundle
type used in its declaration:
pcb-bundle i2c:
port sda
port scl
pcb-module mcu:
port data : i2c
In this example, we define the i2c
bundle. Notice that this bundle is constructed from same port
statements defined above.
The data
port is then constructed with the bundle name i2c
as the type.
The individual signals of the data
port are accessible using dot
notation:
pcb-module top-level :
inst U1 : mcu
println(port-type(mcu.data.sda))
; Prints:
; [SinglePin object]
Bundle Array Port
As with the single pin, we can construct port arrays of bundle types:
pcb-bundle i2c:
port sda
port scl
pcb-module mcu:
port busses : i2c[3]
In this example, we construct a contiguous array of 3 I2C bus ports. We can access the individual signals of these busses as well:
pcb-module top-level :
inst U1 : mcu
println(port-type(mcu.busses[0].scl))
; Prints:
; [SinglePin object]
Port Types
Each port has a type associated with it. That type can be accessed with the function port-type. This function returns a PortType
instance that can be one of the following types:
SinglePin
- A port instance of typepin
Bundle
- A port instance of type BundlePortArray
- An array of port instances
We would typically use the result of this function with a match statement:
pcb-module amp:
port vout : pin[4]
match(port-type(vout)):
(s:SinglePin): println("Single")
(b:Bundle): println("Bundle")
(a:PortArray): println("Array")
This structure allows a different response depending on the type of port.
The Bundle
PortType
The Bundle
port type provides the user with access to the type of bundle that was used to construct this port:
pcb-bundle i2c:
port sda
port scl
pcb-module mcu:
port bus : i2c
pcb-module top-level:
inst U1 : mcu
match(port-type(U1.bus)):
(b:Bundle):
println("Bundle Type: %_" % [name(b)])
(x): println("Other")
; Prints:
; Bundle Type: i2c
Notice that the b
object is a reference to the pcb-bundle i2c
definition. This provides a convenient way to check if a given port matches a particular bundle type.
Walking a PortArray
It is often useful to walk a PortArray
instance and perform some operation on each of the SinglePin
instances of that PortArray
. Because PortArray
instances can be constructed from either single pins or bundles, they can form arbitrarily complex trees of signals. To work with trees of this nature, recursion is the tool of choice.
Note: This is a more advanced example with recursion. Fear not brave electro-adventurer - These examples will serve you well as you become more comfortable with the JITX Environment.
defn walk-port (f:(JITXObject -> False), obj:JITXObject) -> False :
match(port-type(obj)):
(s:SinglePin): f(obj)
(b:Bundle|PortArray):
for p in pins(obj) do:
walk-port(f, p)
pcb-module top-level:
port bus : i2c[3]
var cnt = 0
for single in bus walk-port :
cnt = cnt + 1
println("Total Pins: %_" % [cnt])
; Prints
; Count: 1
; Count: 2
; Count: 3
; Count: 4
; Count: 5
; Count: 6
The i2c
bundle has 2 pins and there are 3 i2c
ports in the array which results in 6 total pins.
In this example, we construct a Sequence Operator
that will allow us to walk the pins of a port. The structure of a Sequence Operator
is:
defn seq-op (func, seq-obj) :
...
Where the seq-obj
is a Sequence
of objects. The for
statement will iterate over the objects in the seq-obj
sequence and then call func
on each of the objects in the sequence. The value returned by this function can optionally be captured and returned.
In our example, walk-port
is a Sequence Operator
that doesn't return any result. Notice how walk-port
has replaced the do
operator that we normally see in a for
loop statement.
So where does the func
function come from then? The for
statement constructs a function from the body
of the for
statement. In this example, the function effectively becomes:
defn func (x:JITXObject) -> False :
cnt = cnt + 1
Notice that this function is a closure. It is leveraging the top-level
context to access the cnt
variable defined before the for
statement.
A similar construction in Python might look like:
cnt = 0
def func(signal):
global cnt
cnt = cnt + 1
for x in walk-port(bus):
func(x)
Where walk-port
would need to be implemented as a generator
in python that constructs a sequence.
Properties
Properties are a flexible way to add data to ports, instances, and nets. We can create and query properties inside components and modules.
The property statement is valid within the following contexts:
Signature
; Get Form
val v1:JITXValue = property(dot-path)
val v2:Maybe<JITXValue> = property?(dot-path)
val v3:JITXValue = property?(dot-path, def-value)
val v4:True|False = has-property?(dot-path)
; Set Form
property(dot-path) = 3.0
The "Get" form of the property
statement allows the user to inspect a particular property of an object. If the requested property doesn't exist, then a NoPropertyWithName
exception will be thrown.
The property?
statement is a "Get" form that will return a Maybe
element or allow for a default value def-value
. This get statement form will not throw an exception if the property doesn't exist. It will either return None()
or it will return the passed def-value
.
The has-property?
statement checks for the existence of a particular property on a particular object. This statement is often used with the eval-when statement.
The "Set" form of the property
statement allows the user to create a new property or override an existing property on an object. The value assigned to a property must be of type JITXValue
.
The argument to the property
statement is a dot notation path or dot-path
. A dot-path
will typically start with the name of an instance, net, or port, and the final element of the dot path will be the identifier for the property.
For example:
pcb-component props-test:
port gnd : pin
property(self.gnd.voltage) = 0.0
In this example, we use the special instance self
to refer to this component instance (ie, an instance of props-test
). The next element on the dot-path
is referring to the gnd
port of the props-test
instance. Finally, the element voltage
is the name of the property that is being set on the gnd
port.
Assigned Types
The value assigned to a property
must be of type JITXValue
. The JITXValue
type includes most of the built-in JITX types like Double
, Int
, Pose
, Toleranced
, String
, etc. It will not necessarily include custom, user-defined types (ie, any type created with deftype
or defstruct
). If you try to set a property with a defstruct
- you will see an exception thrown that looks something like:
Cannot call function 'set-property' with given arguments: Argument 4: Passing type 'CustomUserType' to expected type 'JITXValue'.
To assign more custom data types, you will need to define a pcb-struct type. This
is similar to the defstruct
style type definitions but adds additional features that fulfill the JITXValue
interface.
Common Component Properties
Below is an example component with common property
statement patterns:
pcb-component TPS62081:
manufacturer = "Texas Instruments"
mpn = "TPS62081DSG"
datasheet = "https://www.ti.com/lit/ds/symlink/tps62082.pdf"
pin-properties:
[pin:Ref | pads:Int ...]
[EN | 1]
[GND | 2]
[MODE | 3]
[FB | 4]
[VOS | 5]
[PG | 6]
[SW | 7]
[VIN | 8]
property(self.EN.threshold) = 1.0
property(self.PG.leakage-current) = typ-max(0.01e-6, 0.1e-6)
property(self.junction-temperature) = min-max(-40.0, 125.0)
This definition defines 3 properties, 2 on ports and 1 on the instance itself.
threshold
andleakage-current
are properties extracted from the datasheet.junction-temperature
is applied to the component via theself
keyword.
Common Module Properties
In the below example, we show a pcb-module
definition using property
statements:
pcb-module switcher :
property(self.buck) = true
port VIN : pin
port VOUT : pin
port GND : pin
property(VIN.max-voltage) = 6.0
inst IC : TPS62081
property(IC.no-clean) = true
property(IC.FB.max-voltage) = 3.6
net local-gnd (GND, IC.GND)
property(local-gnd.voltage) = 0.0
In the module context, we have the opportunity to apply properties to:
- The module instance itself via the
self
identifier - The ports of the module instance - eg
VIN
,VOUT
- The component instances defined in the module - eg
IC
in this example. - Ports of the component instances in the module - eg
IC.FB
. - The nets of the module instance - eg
local-gnd
ref-label
The ref-label
statement is a tool for creating the reference designator Text
shape from outside a component's land pattern.
This is a tool primarily used by the importer that allows the JITX runtime to place components and their reference designators accurately on the board.
This statement is only valid in the pcb-module
context.
Signature
ref-label(<INST>) = <Text>
<INST>
- A ref to an instance of a component in the currentpcb-module
context.<Text>
- AText
shape instance that describes the features of the reference designator for this component instance.- The
Text
object will define the location, pose, size, and font for the reference designator. - The string value of this
Text
object must be the literal string>REF
- otherwise an exception is thrown.
- The
Usage
As mentioned above, this statement is primarily a tool for the importer. A typical example will look like:
pcb-module top-level:
...
ref-label(self.MCU.osc) = Text(">REF", 1.0, C, loc(18.380, 5.080, 90.000), "", TrueTypeFont, false, false)
In this example the component instance is a public component instance osc
of a sub-module named MCU
. Notice the use >REF
as the text literal string. This is a template value for the reference designator and will be replaced by the JITX runtime when the design compiles.
The C
argument in this context is the Anchor parameter. The anchor indicates the origin of the text object. In this case C
indicates "Center."
Reference designator
The reference-designator
statement can be used to hardcode the reference designator of a pcb-component instance in a pcb-module
definition.
This statement is only valid within the pcb-module
context.
Signature
reference-designator(<INST>) = <String>
<INST>
- Component Instance in thepcb-module
context. It can optionally be apublic
component instance of a child sub-module.<String>
- String expression that will replace the reference designator for this instance.
Usage
This statement is primarily a tool for use by the importer. You will likely see something like this in most imported projects:
public defn add-main-reference-designators () -> False :
inside pcb-module :
...
reference-designator(self.CM4_HighSpeed.R-7) = "R17"
reference-designator(self.CM4_HighSpeed.R-8) = "R10"
reference-designator(self.CM4_HighSpeed.R-9) = "R1"
reference-designator(self.CM4_HighSpeed.RT9742GGJ5) = "U18"
reference-designator(self.CM4_HighSpeed.RT9742SNGV) = "U12"
...
In this case - the importer knows the structure of sub-modules and components, so it can create a ref to particular components at various depths in the tree and then assign them known designators.
Require
require
statements can be used in coordination with supports statements to automate pin assignment. When we use a require
statement it creates an abstract port
. We can use this abstract port like any other port and JITX will handle mapping that abstract port to a concrete port on a component.
The require
statement is valid in the following contexts:
Signature
; Implicit `self` form
require <NAME>:<TYPE>
require <NAME>:<TYPE>[<ARRAY>]
; Explicit form
require <NAME>:<TYPE> from <INST>
require <NAME>:<TYPE>[<ARRAY>] from <INST>
<NAME>
- Name of the created abstract port in this context. This must be a unique symbol name in the current context.<TYPE>
- TheBundle
type for the requested abstract port.<ARRAY>
- Optional Array Initializer for constructing an array of abstract port.<INST>
- TheInstance
from which we are requesting abstract port.- In the
Implicit
form, this value isself
by default. - In the
Explicit
form, we must provide a ref to a specific module or component instance in the current context.
- In the
Usage
Basic Pin Assignment
pcb-bundle i2c:
port sda
port scl
pcb-module top-level:
inst mcu : stm32f405G7
require bus:i2c from mcu
inst sensor : temp-sensor
net (bus, sensor.i2c-bus)
inst R : chip-resistor(4.7e3)[2]
net (bus.sda, R[0].p[1])
net (bus.scl, R[1].p[1])
This is a typical use case for pin assignment in a microcontroller circuit. Here we are requesting one of the 3 available I2C ports on the SM32F405 and constructing an abstract port that will map to one of them depending on what other require
statements exists as well as the board conditions.
The bus
abstract port can be used like any other port on a component or module. We can use the net
statement to connect it to other ports. We can use dot
notation to connect to individual pins of the abstract port.
Abstract Port Array
pcb-bundle gpio:
port p
pcb-module top-level:
inst mcu : stm32f405G7
val num-sw = 4
require sw-inputs:gpio[ num-sw ] from mcu
inst switches : momentary-switch[ num-sw ]
for i in 0 to num-sw do:
net (sw-inputs[i].p, switches[i].p[1])
Like other inst
or net
declarations, we can construct an array of abstract ports withe []
syntax. Here we construct 4 GPIO abstract ports requested from the mcu
instance.
We can then connect these individual gpio pins to other instance ports, like the momentary-switch
instances.
Use of require
inside supports
We often want to cascade the construction of abstract ports by using require
statements inside supports
statements.
pcb-component stm32:
port PA : pin[32]
...
pcb-bundle I2C0_SDA:
port p
supports I2C0_SDA:
option:
I2C0_SDA.p => self.PA[1]
option:
I2C0_SDA.p => self.PA[7]
pcb-bundle I2C0_SCL:
port p
supports I2C0_SCL:
option:
I2C0_SCL.p => self.PA[2]
option:
I2C0_SCL.p => self.PA[6]
supports i2c:
require sda0:I2C0_SDA
require scl0:I2C0_SCL
i2c.sda => sda0
i2c.sda => scl0
pcb-module top-level:
inst mcu : stm32
require bus:i2c from mcu
In this example, we define ad-hoc pcb-bundle
definitions I2C0_SDA
and I2C0_SCL
that are only accessible within this component's context. The supports
statements for these two bundle types effectively make private pending ports
that can only be used within this context.
Finally - we make a supports i2c:
statement that constructs an externally accessible pending port for the i2c
bundle type. The bus
abstract port that we are ultimately able to connect to the rest of our system consists of:
bus.sda
=>mcu.PA[1]
ORmcu.PA[7]
bus.scl
=>mcu.PA[2]
ORmcu.PA[6]
Notice that in the supports i2c:
statement, the require
statement uses the Implicit
form (ie, the lack of a from <INST>
part of the statement). This means that this require
statement is targeting self. This statement is exactly the same as:
require sda0:I2C0_SDA from self
Check out restrict
There is also the restrict statement which provide another method of defining the constraints for the pin assignment problem. The restrict
statement operates on the abstract port
instances defined by the require
statement.
Restrict
restrict
can be used in coordination with require
to add specific constraints on which pins are valid. We can specify all pins in a bundle to come from a specific IO bank, or that a timer has some advanced feature, or has a specific electrical property. The concept is that while supports statements are additive - the restrict
statements are subtractive.
The other useful feature for restrict
is that it is a constraint that can be added at point of use. This allows the user to construct a general purpose component that can be used in many circuits, and then customize the pin assignment problem for a particular board design.
Signature
restrict(<REQ-PORT>, <FUNC>)
<REQ-PORT>
- Reference to aSinglePin
abstract port
defined by the require statement.<FUNC>
- Reference to a function that takes aPort
argument and returns a boolean (ie,True|False
)- This function must return
true
if the passedPort
should be an option in the solution to the pin assignment problem. - This function must return
false
if thisPort
should be rejected from the solution to the pin assignment problem.
- This function must return
Usage
When we use the restrict
statement, we are typically evaluating the properties
of the pins of a component or module. Here is a trivial example:
pcb-bundle gpio:
port single
pcb-module restrict-a-pin-set :
port p : pin[2]
property(p[0].active) = true
property(p[1].active) = false
for i in 0 to length(p) do:
supports gpio :
gpio.single => p[i]
require g:gpio from self
restrict(g.single,
fn (mypin) :
property(mypin.active))
In this example, the module has a port array p
with two single pins. We then apply an active
property to both of these single pins. This active
property is what our restrict
statement is going to check. Later the restrict
statement checks whether the active
property is true
and only allows p[0]
to be selected in the pin assignment solution.
Notice that the restrict
statement is referring one of the SinglePin
ports of the abstract port g
of type gpio
bundle. The restrict
statement must refer to an abstract port
(ie, a port defined by a restrict statement). If the restrict
were to refer to a module or component port, this would be an error.
Here we are using an anonymous function (kind of like a lambda in python) to implement the restriction function:
fn (mypin) :
property(mypin.active)
If you squint - this kind of looks like a function definition:
defn my-restrict-func (mypin) :
property(mypin.active)
In fact, there is no reason you couldn't use that style of definition, you would just need to define it ahead of time:
require g:gpio from self
defn is-active? (mypin) :
if has-property?(x.active):
property(x.active)
else:
false
restrict(g.single, is-active?)
NOTE - It is a good practice to make sure that all of the pins that are going to participate in this
restrict
functionality define theproperty
that will be examined in the restrict function. In this example, theactive
property is applied to all of the pins inp
- not just the pins inp
that areactive=true
.In the above example, the
has-property?
function is a useful tool to detect if/when a pin is missing a property and take a reasonable default action.
Why use a restrict
?
For the most part, any restrict
statement could also be implemented with a supports
statement. The restrict
is kind of like the DeMorgan's equivalent version.
So what is the purpose of the restrict
statement if we already have supports
statements?
- Sometimes the
restrict
statement version is less verbose than the equivalentsupports
statements. - Often times the
supports
statement that satisfies a particularrequire
statement are not co-located.- Placing a
restrict
statement in the same scope as therequire
statement can often make the code more readable. - It can be easier to document the reason for a particular
restrict
when it is co-located with therequire
- Placing a
- The
supports
statements are often broad constraints that apply generally to a particular component.- The
restrict
statement is often a refinement of those broad constraints for your particular application. - This also means that we can define a general component that gets used in many circuits, and then add application specific constraints at point of use with a
restrict
- The
Examples
Gigabit Transceivers
In many FPGAs, there are often one or more classes of serializers/transceivers for high speed communications. These typically are differential pair transceivers and result in the use of a diff-pair
bundle for all of them.
To differentiate between these different differential pairs, we can set a property
on those differential pairs and use a restrict
statement. Full listing of the example code.
defn is-gigabit-transceiver (x) -> True|False :
if has-property?(x.gigabit-transceiver):
property(x.gigabit-transceiver)
else:
false
public pcb-module gigabit-restrict-example:
inst fpga : Xilinx-FPGA
inst conn : VPX-conn
require h-dp:diff-pair from fpga
require c-dp:diff-pair from conn
for dp in [h-dp, c-dp] do:
restrict(dp.P, is-gigabit-transceiver)
; Connect the two high-speed capable differential pairs.
net (h-dp, c-dp)
This is the top-level of the example demonstrating the use of the restrict
statement with the helper function is-gigabit-transceiver
.
Notice that the restrict
statement can be placed anywhere within the scope of this pcb-module
. The only requirement is that it can uniquely identify which abstract port
that the statement applies to.
In the restrict
statement, we apply the restriction only to the dp.P
, or the positive side of the differential pair. We don't have to add the dp.N
restriction because it is basically implied by the supports diff-pair
constraint. It wouldn't hurt anything to add it, but it would make the pin assignment problem more computationally complex.
The property gigabit-transceiver
is applied in the pcb-component
definition for the FPGA and the connector. Here is a slimmed down example from the FPGA:
public pcb-component Xilinx-FPGA :
name = "Xilinx"
description = "Excerpt of an IO Bank & High Speed Transceiver"
manufacturer = "Xilinx"
pin-properties :
[pin:Ref | pads:Ref ... | side:Dir | bank:Int]
[IO_L2N | C[3] | Right | 0 ]
[IO_L2P | C[2] | Right | 0 ]
[MGTHRXN0 | D[3] | Right | 0 ]
[MGTHRXP0 | D[4] | Right | 0 ]
pin-properties :
[pin:Ref | clock-capable:(True|False) | gigabit-transceiver:(True|False)]
[IO_L2N | false | false]
[IO_L2P | false | false]
[MGTHRXN0 | false | true]
[MGTHRXP0 | false | true]
make-box-symbol()
assign-landpattern(FPGA-pkg)
supports diff-pair:
diff-pair.P => self.IO_L2P
diff-pair.N => self.IO_L2N
supports diff-pair:
diff-pair.P => self.MGTHRXP0
diff-pair.N => self.MGTHRXN0
Here we use two pin-properties
tables. The first pin-properties
table defines the pins, pads, and bank associations. The second pin-properties
table defines other properties related to each of those pins. In particular - it defines the gigabit-transceiver
property as a boolean value in the 3rd column. Notice that these properties are on a per-pin basis - not on the bundle.
At the end of the component, we define two diff-pair
supports. Neither of these supports
statements care about the gigabit-transceiver
properties. In absence of the restrict
statements, a require
statement for a diff-pair
bundle might select either of these two diff-pair
bundles.
Example rendering in the board view:
Notice that the differential pair rat's nest only connects to the MGTHRXN0/MGTHRXP0
pair on the FPGA.
#use-added-syntax(jitx)
defpackage fpga_gigabit:
import core
import jitx
import jitx/commands
import ocdb/utils/bundles
import ocdb/utils/box-symbol
public pcb-component VPX-conn :
name = "ASP-134488-01"
description = "Vita 57.1 HPC FMC connector"
manufacturer = "Samtec"
mpn = "ASP-134488-01"
pin-properties :
[pin:Ref | pads:Ref ... | side:Dir | bank:Int]
[dp0-c2m-n | C[3] | Right | 0 ]
[dp0-c2m-p | C[2] | Right | 0 ]
[dp1-c2m-n | A[23] | Right | 0 ]
[dp1-c2m-p | A[22] | Right | 0 ]
pin-properties :
[pin:Ref | clock-capable:(True|False) | gigabit-transceiver:(True|False)]
[dp0-c2m-n | false | true]
[dp0-c2m-p | false | true]
[dp1-c2m-n | false | false]
[dp1-c2m-p | false | false]
make-box-symbol()
assign-landpattern(ASP-134488-01-pkg)
supports diff-pair:
diff-pair.P => self.dp0-c2m-p
diff-pair.N => self.dp0-c2m-n
supports diff-pair:
diff-pair.P => self.dp1-c2m-p
diff-pair.N => self.dp1-c2m-n
public pcb-component Xilinx-FPGA :
name = "Xilinx"
description = "Excerpt of an IO Bank & High Speed Transceiver"
manufacturer = "Xilinx"
pin-properties :
[pin:Ref | pads:Ref ... | side:Dir | bank:Int]
[IO_L2N | C[3] | Right | 0 ]
[IO_L2P | C[2] | Right | 0 ]
[MGTHRXN0 | D[3] | Right | 0 ]
[MGTHRXP0 | D[4] | Right | 0 ]
pin-properties :
[pin:Ref | clock-capable:(True|False) | gigabit-transceiver:(True|False)]
[IO_L2N | false | false]
[IO_L2P | false | false]
[MGTHRXN0 | false | true]
[MGTHRXP0 | false | true]
make-box-symbol()
assign-landpattern(FPGA-pkg)
supports diff-pair:
diff-pair.P => self.IO_L2P
diff-pair.N => self.IO_L2N
supports diff-pair:
diff-pair.P => self.MGTHRXP0
diff-pair.N => self.MGTHRXN0
defn is-gigabit-transceiver (x) -> True|False :
if has-property?(x.gigabit-transceiver):
property(x.gigabit-transceiver)
else:
false
public pcb-module gigabit-restrict-example:
inst fpga : Xilinx-FPGA
inst conn : VPX-conn
require h-dp:diff-pair from fpga
require c-dp:diff-pair from conn
for dp in [h-dp, c-dp] do:
restrict(dp.P, is-gigabit-transceiver)
; Connect the two high-speed capable differential pairs.
net (h-dp, c-dp)
; Land Patterns
defn bga-pkg (pitch:Double, pad-diam:Double, n-pads:[Int Int], courtyard:[Double Double], omit-pads:Tuple<Ref>) :
inside pcb-landpattern :
pcb-pad pin-pad :
type = SMD
shape = Circle(pad-diam / 2.0)
layer(Paste(Top)) = Circle(pad-diam / 2.0)
layer(SolderMask(Top)) = Circle(pad-diam / 2.0)
defn make-pad (r:Int, c:Int) :
val x = ((to-double(n-pads[0]) - 1.0) / -2.0 + to-double(c)) * pitch
val y = ((to-double(n-pads[1]) - 1.0) / 2.0 - to-double(r)) * pitch
; I IS MISSING DELIBERATELY BECAUSE OF SAMTEC's ANNOYING LETTER SCHEME, but O is present.
val letter = ["A" "B" "C" "D" "E" "F" "G" "H" "J" "K" "L" "M" "N" "O" "P" "Q" "R" "S" "T" "U" "V" "W" "X" "Y" "Z"][r]
val name = Ref(letter)[c + 1]
if not contains?(omit-pads, name) :
pad (name) : pin-pad at loc(x, y)
for c in 0 to n-pads[0] do :
for r in 0 to n-pads[1] do :
make-pad(r, c)
layer(Courtyard(Top)) = Rectangle(courtyard[0], courtyard[1])
layer(Silkscreen("pol", Top)) = Circle(-0.3 - courtyard[0] / 2.0, courtyard[1] / 2.0, 0.127)
pcb-landpattern ASP-134488-01-pkg :
bga-pkg(1.27, 0.64, [40 10], [55.78 14.68], [])
; Alignment holes
let:
val xdim = 27.19
val yoff = -3.05
val align-hole-diam = 1.27
; Aligment hole on the right is centered vertically
; Aligment hole on the left is offset vertically
layer(Cutout()) = Circle( xdim, 0.0, align-hole-diam / 2.0)
layer(Cutout()) = Circle( -1.0 * xdim, yoff, align-hole-diam / 2.0)
let:
val xdim = 31.49695
val yoff = 2.006
val standoff-hole-diam = 3.2
; Standoff holes
layer(Cutout()) = Circle( xdim, yoff, standoff-hole-diam / 2.0)
layer(Cutout()) = Circle( -1.0 * xdim, yoff, standoff-hole-diam / 2.0)
pcb-landpattern FPGA-pkg :
bga-pkg(1.0, 0.55, [23, 23], [24.0, 24.0], [])
; Set the design name - a directory with this name will be generated under the "designs" directory
; the board - a Board object
; [optional] rules - the PCB design rules (if not givn default rules will be used)
; [optional] vendors - Strings or AuthorizedVendors (if not give default vendors will be used)
; [optional] quantity - Minimum stock quantity the vendor should carry (if not give default quantity will be used)
public defn setup-design (name:String, board:Board
--
rules:Rules = ocdb/utils/defaults/default-rules
vendors:Tuple<String|AuthorizedVendor> = ocdb/utils/design-vars/APPROVED-DISTRIBUTOR-LIST
quantity:Int = ocdb/utils/design-vars/DESIGN-QUANTITY
bom-columns:Tuple<BOMColumn> = ocdb/utils/design-vars/BOM-COLUMNS) :
set-current-design(name)
set-board(board)
set-rules(rules)
set-bom-vendors(vendors)
set-bom-design-quantity(quantity)
set-bom-columns(bom-columns)
set-paper(ANSI-A4)
set-export-backend(`altium) ; set the CAD software for export to be altium (also supported: `kicad)
val board-shape = RoundedRectangle(100.0, 100.0, 0.25)
setup-design(
"gigabit-restrict-example",
ocdb/utils/defaults/default-board(ocdb/manufacturers/stackups/jlcpcb-jlc2313, board-shape)
)
; Set the schematic sheet size
set-paper(ANSI-A)
; Set the top level module (the module to be compile into a schematic and PCB)
set-main-module(gigabit-restrict-example)
; Use any helper function from helpers.stanza here
; run-check-on-design(my-design)
; View the results
view-board()
view-schematic()
; view-design-explorer()
; view-bom(BOM-STD)
Schematic Group
The schematic-group
statement is a tool to organize the components and modules in a JITX design. This statement is the primary tool for constructing the module hierarchy in the schematic.
This statement is only valid in the pcb-module
context.
Signature
There are two major forms of the schematic-group
statement:
- Assignment
- Same Group Association
Schematic Group Assignment
; Assign Schematic Group
schematic-group(<INST>) = <Ref>
schematic-group(<Seqable<INST>>) = <Ref>
; Assign Schematic Group to Symbol Unit
schematic-group(<INST>, <UNIT:Int>) = <GROUP:Ref>
<INST>
- A component or module instance as defined in thispcb-module
context.<Seqable<INST>>
- ASeqable
of instances that allow schematic group assignment for mutiple instances at a time.<Group:Ref>
- The name of a schematic group. This must be a validRef
in the JITX runtime. This will be used as part of the schematic group names in the Schematic UI.<UNIT:Int>
- Symbol Unit Index for identifying sub-parts of a multi-part symbol. Note that this value is not necessarily the same as theInt|Ref
used to identify a symbol unit bank in the pin-properties table.
Same Schematic Group Association
; Copy Schematic Group
<SCH-GROUP-STMT> = schematic-group(<ANCHOR>)
<SCH-GROUP-STMT> = schematic-group(<ANCHOR>, <UNIT:Int>)
<SCH-GROUP-STMT>
- This has the same syntax as the left side of the schematic group assignment statements described above. This identifies the component instance to which a group will be assigned.<ANCHOR>
- This identifies a component instance in the current module context that already has a group. This statement assigns this component existing groupt to the instance in the<SCH-GROUP-STMT>
<UNIT:Int>
- Optional Symbol Unit Index to refer to a particular sub-part of theANCHOR
instance.
Usage
The schematic-group
statement is a tool for organizing the schematic by grouping like components together. For example, when creating a wrapper for a complex component like an FPGA, you might have many bypass capacitors. It can be useful to group these capacitors together and work with them as a block.
pcb-module FPGA :
inst U1 : Xilinx-XC7
for i in 0 to length(U1.VCCINT) do:
val cap = bypass-cap-strap(U1.VCCINT[i], U1.GND, 0.1e-6)
schematic-group(cap) = bypass-caps-VCCINT
In this example, we instantiate multiple bypass capacitors, one for each of the VCCINT
pins of the FPGA, and then add each capacitor to the bypass-caps-VCCINT
schematic group. Notice that the group is assigned to a component instance not the definition.
This makes an organizational group in the schematic for these bypass capacitors. This bypass-caps-VCCINT
group will be a child group of the FPGA
parent group.
Multiple Component Assignment
To make schematic group assignment less verbose, the user can assign a group to multiple component instances simultaneously.
pcb-module some-circuit:
inst amp : SingleOpAmp
inst R1 : chip-resistor(13.3e3)
inst R2 : chip-resistor(47.0e3)
inst C1 : ceramic-cap(0.1e-6)
...
schematic-group([amp, R1, R2]) = anti-alias-filter
schematic-group(C1) = anti-alias-filter
This will construct a group anti-alias-filter
and add the amp
, R1
, R2
, and C1
component instances to that group. Notice that once a schematic group is created, it is not closed. You can add additional components to that group by adding additional schematic-group
statements.
Multi-part Symbol Components
For components with multi-part schematic symbols, an optional second argument can be provided which will map only a particular symbol unit by Int
. (Currently, symbol units using Ref
identifiers are not supported).
This is often useful when dealing with large, complex components like FPGAs (see A2F200M3F-FGG256I.stanza). For the Microsemi A2F200 FPGA, the pcb-component
defines multiple banks, such as the supply
, jtag
, and analog
banks.
inst fpga : ocdb/components/microsemi/A2F200M3F-FGG256I/component
schematic-group(fpga, 0) = power-config ; supply
schematic-group(fpga, 1) = sensors ; analog
schematic-group(fpga, 2) = power-config ; osc
schematic-group(fpga, 3) = power-config ; jtag
In this example, the second argument to schematic-group
is the symbol unit index for the bank in question. The symbol unit index is incremented monotonically for every bank.
If the optional second argument is not provided for a multi-part symbol, then all parts of the symbol for that component instance will be associated with the given group.
Implicit Schematic Group for pcb-module
For each pcb-module
definition, there is an implicit schematic-group
statement. It is effectively:
pcb-module mod-name :
...
schematic-group(self) = mod-name
This means that for most circuits you won't need to use the schematic-group
statement at all. Just by constructing a design using pcb-module
definitions, we can create a design hierarchy that makes sense.
This also means that any manually created schematic-group
statements inside this pcb-module
will effectively be prefixed with mod-name
in their Ref
.
Same Group Association
The same group association form of this statement looks a bit funny. On its face, it doesn't look super useful. Where this form of group assignment shines is when you are writing a generator function.
defn add-some-pullups (comp:Instance, rail:JITXObject, resistance:Double = 4.7e3):
inside pcb-module:
inst R : chip-resistor(resistance)[2]
for i in 0 to length(R) do:
net (R[i], rail)
net (comp.sda, R[0])
net (comp.scl, R[1])
schematic-group([R[0], R[1]]) = schematic-group(comp)
The idea here is that we have a function that accepts a component instance as an argument and adds some new components, in this case pull-up resistors, to that component. We want those new pull-up resistors to end up in the same schematic group as the comp
instance. But we have a problem - we don't know what group the user might have used in the pcb-module
context where comp
was instantiated.
Use the "Same Group Association" form, we can pull the group association from the existing component.
Short Trace
The short-trace
statement identifies two component pads that have a high affinity to one another. This most obvious example is bypass capacitors applied to supply pins.
This information is used in multiple places:
- The placer uses this information to create an initial layout that places these pads close together.
- The Board UI uses this information to make a unique rats nest representation.
This statement is only valid in the pcb-module
context.
Signature
short-trace(<REF-1>, <REF-2>)
<REF-*>
- The arguments toshort-trace
are expected to be refs toSinglePin
ports of a component instance.- Each port must be connected to the same net.
- Providing a ref to a
pcb-module
port will have no effect. The ref must be to a port on a component instance that maps to a physical pad of the component's land pattern.
Usage
The most common usage pattern for short-trace
is for bypass capacitors.
pcb-module microcontroller:
inst U1 : MCU
inst C5 : ceramic-cap(0.1e-6)
net VDD (C5.p[1], U1.VDD)
net GND (C5.p[2], U1.GND)
short-trace(C5.p[1], U1.VDD)
If we examine what this looks like in the board view, we will see something like this:
Notice that the connection between C5 and the QFN package component, we see that the rat's nest has a blue hue to it. This is the signature for short-trace
connections in the board view.
This is a very useful tool during layout for determining which bypass capacitors should be placed where. Instead of referring back to the schematic or the datasheet for a part, we can immediately know where a particular cap needs to go.
Supports
supports
statements can be used in coordination with require statements to automate pin assignment. We use supports
to describe valid ways pins can be assigned. A supports
statement creates a pending port
.
Often supports
are used to describe pin mappings on a processor (e.g. a i2c
peripheral can map to pins here or here). The support mechanism is very flexible and can make arbitrarily complex mapping constraints.
The supports
statement is valid in the following contexts:
Signature
; Single Option Form
supports <TYPE> :
<REQUIRE-1>
...
<TYPE>.<PORT-1> => <ASSIGN-1>
<TYPE>.<PORT-2> => <ASSIGN-2>
...
; Multiple Option Form
supports <TYPE> :
<REQUIRE-1>
...
option:
<REQUIRE-1>
...
<TYPE>.<PORT-1> => <ASSIGN-1>
<TYPE>.<PORT-2> => <ASSIGN-2>
...
option:
<REQUIRE-1>
...
<TYPE>.<PORT-1> => <ASSIGN-3>
<TYPE>.<PORT-2> => <ASSIGN-4>
...
...
<TYPE>
- Bundle type that identifies what type of pending port will be constructed<REQUIRE-1>
- Optionalrequire
statements that can be used to created nested relationships.<TYPE>.<PORT-1>
- Target statement for one of theSinglePin
ports of<TYPE>
.<ASSIGN-1>
- A ref to aabstract port
, component/module port, or- The
<TYPE>.<PORT-1> => <ASSIGN-1>
statements areassignments
.
Overview
The supports
statement provide a means of constructing a pending port
. A pending port on a component or module instance is used to satisfy require
statement. A pending port isn't an object or instance like a port
, abstract port
, or net
. It is more ethereal. It provides a means of defining the constraints for making a particular connection as opposed to being a particular port or pin.
Supports Example
pcb-bundle reset:
port p
pcb-component mcu:
pin-properties:
[pin:Ref | pads:Int ...]
[RESET_n | 5 ]
supports reset:
reset.p => self.RESET_n
In this case, the support reset:
statement is constructing a single pending port of type reset
. It has one port with only one matching option, self.RESET_n
. This support reset:
statement acts like an interface definition for the reset signal.
Option Statements
The option
statement is a mechanism by which we can define a set of acceptable options for this bundle type. You can think of a supports
with option
statements as a big element-wise OR
gate.
Option Example
pcb-bundle gpio:
port p
pcb-component mcu:
port PA : pin[16]
supports gpio :
option :
gpio.p => self.PA[1]
option :
gpio.p => self.PA[4]
This supports
statement constructs a single pending port
of type gpio
. This pending port
can map to either:
self.PA[1]
ORself.PA[4]
In this case, there are only 2 options, but there is no limit. We could add an arbitrary number of option
statements.
An Arbitrary Number You Say...
"An arbitrary number seems nice but wouldn't that get rather tedious?" - well yes, but actually no.
This is where typical programming control flow and loop constructs, like for
, if
, and while
, come in. If we take our previous example and expand it from 2 options to 16 options, it might look something like this:
pcb-bundle gpio:
port p
pcb-component mcu:
val num-pins = 16
port PA : pin[ num-pins ]
supports gpio :
for i in 0 to num-pins do:
option:
gpio.p => self.PA[i]
Here - again - we only get 1 gpio
pending port from this statement. But now that one gpio can use any of the available IO pins on the PA
port of the microcontroller. Progress - now let's open it up a bit more...
Less is More
If we take one more crack at this example and expand our desire from 1 gpio
pending port to 16 gpio
pending ports with full pin-assignment across the port, we might end up here:
pcb-bundle gpio:
port p
pcb-component mcu:
val num-pins = 16
port PA : pin[ num-pins ]
for i in 0 to num-pins do:
supports gpio :
gpio.p => self.PA[i]
We don't actually need the option:
statement at all to achieve our goal. Just constructing 16 supports gpio:
statements is sufficient to create a full cross-bar. Lets consider an example of how this gets used:
inst host : mcu
require switches:gpio[4] from host
This statement basically says, "Give me 4 gpio ports - I don't care which ones right now, we'll decide that later. They just need to be GPIO ports."
This basically makes an implicit OR
between all of the defined gpio
type pending ports
that aren't used for some other purpose.
Don't Forget All the Ports
Up to now we've been talking about bundles with a single port, but we can implement supports
statements on arbitrarily complex bundles. The key things to remember are:
- Every port of a bundle must have an assignment to form a valid
supports
statement. - In each
option:
statement, every port of a bundle must have an assignment to form a validoption
statement.
Invalid Support Example
Consider the following as an example that breaks this rule:
pcb-bundle spi:
port sclk
port poci
port pico
pcb-component mcu:
port PB : pin[16]
supports spi:
spi.sclk => self.PB[0]
option:
spi.poci => self.PB[1]
spi.pico => self.PB[2]
option:
spi.poci => self.PB[3]
spi.pico => self.PB[4]
On its face, this looks like a very reasonable structure, but unfortunately it doesn't follow the signatures defined above. The likely goal of this statement is spi.sclk => self.PB[0]
for all options. We can implement that logic with explicit statements in each option:
Correct Implementation
supports spi:
option:
spi.sclk => self.PB[0]
spi.poci => self.PB[1]
spi.pico => self.PB[2]
option:
spi.sclk => self.PB[0]
spi.poci => self.PB[3]
spi.pico => self.PB[4]
Adding Properties on Assignment
It is often useful to annotate pins that are selected for a particular support function. To support this, properties can be supplied in either the supports
statement or the option
statements:
pcb-bundle gpio:
port p
pcb-bundle i2c:
port sda
port scl
pcb-component mcu:
val num-pins = 16
port PA : pin[ num-pins ]
for i in 0 to num-pins do:
supports gpio :
gpio.p => self.PA[i]
property(self.PA[i].is-gpio?) = true
supports i2c:
option:
i2c.sda => self.PA[4]
i2c.scl => self.PA[5]
property(self.PA[4].is-i2c?) = true
property(self.PA[5].is-i2c?) = true
option:
i2c.sda => self.PA[11]
i2c.scl => self.PA[12]
property(self.PA[11].is-i2c?) = true
property(self.PA[12].is-i2c?) = true
The property
statement at the end of the support/option statement only activates if this support/option is selected by the pin assignment solver. Note that this action does not necessarily happen at compile time. It may happen days or weeks later when a component in the layout moves or a route is completed.
Modules Work Too!
Up to now, we've primarily been referencing components, but don't forget that these techniques apply to pcb-module
as well. In fact, modules are where pin assignment can really shine.
Whenever you have multiple components that have a strict constraint between them, use of supports
statements in the module can help abstract the details of these complex constraints. They make our code more readable and grok-able.
Let's consider an example where we want to make multiple simple RC filters. Here is a module definition for an RC filter.
This example is intentionally simplified and does not contain a lot of the detailed engineering you might want in a real circuit, like voltage specs, tolerances, etc. This is primarily for demonstration purposes.
pcb-module low-pass (freq:Double, R-val:Double = 10.0e3) :
port vin : pin
port vout : pin
port gnd : pin
val C-val = 1.0 / ( 2.0 * PI * R-val * freq)
val C-val* = closest-std-val(C-val, 0.2)
inst R : chip-resistor(R-val)
inst C : ceramic-cap(C-val*)
net (vin, R.p[1])
net (R.p[2], C.p[1], vout)
net (C.p[2], gnd)
This filter has a 3-pin interface: vin
, vout
, and gnd
We want to construct a module that allows us to instantiate some number of these filters, but not necessarily lock ourselves into a specific pin assignment. This is where the supports
statements come in at the module level.
We do need to make sure that the input and output pins match though. It would do us no good if the input for channel 1 matched to the output of channel 3. This is where pass-through
bundle comes into play:
pcb-bundle pass-through:
port A : pin
port B : pin
pcb-module multi-lpf (ch:Int, freq:Double) :
port gnd : pin
inst lpfs : low-pass(freq)[ch]
for i in 0 to ch do:
net (gnd, lpfs[i].gnd)
for i in 0 to ch do:
supports pass-through:
pass-through.A => lpfs[i].vin
pass-through.B => lpfs[i].vout
The pass-through
bundle defines two ports that are matched. These are our vin/vout
pair.
Then we construct a pending port of type pass-through
for each channel of the multi-lpf
definition. With this construction, we get the ability to use the RC filters in any channel location on the board.
Note that the above video just shows the first solution that the Pin Assignment solver was able to deduce. You can route to any valid pin as defined by the require/supports
statements of the pin assignment problem.
Here is a link to a complete code example
Nested Require/Support Statements
To make complex constraints, we will often use a cascade of supports statements. We generally call this a Nested
require/supports statement. This structure allows us to break down a complex constraint into several simpler constraints and combine them together.
public val UART:Bundle = uart([UART-RX UART-TX UART-RTS UART-CTS])
public pcb-component my-component :
port PA : pin[10]
; Internal (Private) Bundle Definition
pcb-bundle io-pin : (port p)
for i in 5 to 10 do:
supports io-pin :
io-pin.p => PA[i]
supports UART :
require pins:io-pin[4]
UART.tx => pins[0].p
UART.rx => pins[1].p
UART.rts => pins[2].p
UART.cts => pins[3].p
Our goal is to create a UART
pending port that uses any of PA[5]
, PA[6]
, PA[7]
, PA[8]
, or PA[9]
as any of the tx
, rx
, rts
, or cts
pins of our UART. This is combinatorics problem.
We accomplish this in two steps:
- Define the set of pins from
PA
that we can select from. - Select from that set to full fill one UART
pending port
We create two kinds of supports
statements:
io-pin
- This is a private bundle type that only exists withinmy-component
.- This defines the set of pins from
PA
that we can select from.
- This defines the set of pins from
UART
- Customized UART bundle with our specific pin configuration.- This implements the "Select N from K" using a nested
require
statement.
- This implements the "Select N from K" using a nested
#use-added-syntax(jitx)
defpackage main :
import core
import math
import jitx
import jitx/commands
import ocdb/utils/generic-components
; Define the shape/size of the board
val board-shape = RoundedRectangle(30.0, 18.5, 0.25)
doc: \<DOC>
RC Low Pass Filter
This is a very crude implementation. This is really
only intended to demonstrate the concept of modules
with multiple components being used as a channel
in pin assignment.
@param freq Corner Frequency for the RC Filter. Must be greater than zero.
@param R-val Resistance value to use by default in the RC filter.
<DOC>
pcb-module low-pass (freq:Double, R-val:Double = 10.0e3) :
port vin : pin
port vout : pin
port gnd : pin
val C-val = 1.0 / ( 2.0 * PI * R-val * freq)
val C-val* = closest-std-val(C-val, 10.0)
inst R : chip-resistor(R-val)
inst C : ceramic-cap(C-val*)
net (vin, R.p[1])
net (R.p[2], C.p[1], vout)
net (C.p[2], gnd)
doc: \<DOC>
Pass Through Connection Bundle
<DOC>
pcb-bundle pass-through:
port A : pin
port B : pin
doc: \<DOC>
Multi-Channel RC Low Pass Filter
@param ch Number of channels to construct
@param freq Cut-off frequency of the filters in Hz. Must be greater than zero.
<DOC>
pcb-module multi-lpf (ch:Int, freq:Double) :
port gnd : pin
inst lpfs : low-pass(freq)[ch]
for i in 0 to ch do:
net (gnd, lpfs[i].gnd)
for i in 0 to ch do:
supports pass-through:
pass-through.A => lpfs[i].vin
pass-through.B => lpfs[i].vout
; Module to run as a design
pcb-module my-design :
net GND
inst J1 : ocdb/components/amphenol/minitek127/component(6)
inst J2 : ocdb/components/amphenol/minitek127/component(6)
net (GND, J1.p[6], J2.p[6])
val chs = 4
inst filters : multi-lpf(chs, 100.0e3)
net (GND, filters.gnd)
require pts:pass-through[chs] from filters
for i in 0 to chs do:
net (J1.p[i + 1], pts[i].A)
net (J2.p[i + 1], pts[i].B)
geom(GND):
copper-pour(LayerIndex(0, Bottom), isolate = 0.2) = board-shape
public defn setup-design (name:String, board:Board
--
rules:Rules = ocdb/utils/defaults/default-rules
vendors:Tuple<String|AuthorizedVendor> = ocdb/utils/design-vars/APPROVED-DISTRIBUTOR-LIST
quantity:Int = ocdb/utils/design-vars/DESIGN-QUANTITY
bom-columns:Tuple<BOMColumn> = ocdb/utils/design-vars/BOM-COLUMNS) :
set-current-design(name)
set-board(board)
set-rules(rules)
set-bom-vendors(vendors)
set-bom-design-quantity(quantity)
set-bom-columns(bom-columns)
set-paper(ANSI-A4)
set-export-backend(`altium) ; set the CAD software for export to be altium (also supported: `kicad)
setup-design(
"jitx-design",
ocdb/utils/defaults/default-board(ocdb/manufacturers/stackups/jlcpcb-jlc2313, board-shape)
)
; Set the schematic sheet size
set-paper(ANSI-A)
; Set the top level module (the module to be compile into a schematic and PCB)
set-main-module(my-design)
; Use any helper function from helpers.stanza here
; run-check-on-design(my-design)
; View the results
view-board()
view-schematic()
view-design-explorer()
view-bom(BOM-STD)
structure
The structure
statement is a means of applying the Routing Structure constraint to a signal topology. This constraint is used by the routing engine to apply the appropriate geometry to signal routes on the board.
This statement is only valid within the pcb-module
context.
These constraints are applied to the endpoints of a signal topology. The routing engine will infer that this constraint must be applied to every segment of the topology between the passed endpoints.
Signature
; Explicit Endpoint Form
structure(<REF-1>, <REF-2>) = <RoutingStructure|DifferentialRoutingStructure>
; KeyValue Form
structure(<EXP>) = <RoutingStructure|DifferentialRoutingStructure>
structure(<EXP-1>, <EXP-2>) = <DifferentialRoutingStructure>
For the Explicit Endpoint
form, the user provides a ref to a set of ports. The requirements are:
- The
<REF-*>
must be a ref to a component, module, or abstract (require) port.- For
RoutingStructure
(Single-Ended):- This port can be a
SinglePin
,Bundle
, orPortArray
- This port can be a
- For
DifferentialRoutingStructure
:- This port must be of type
Bundle
. - The
Bundle
type of this port must contain 2SinglePin
ports. - This is typically a diff-pair bundle
- This port must be of type
- For
- The types of
<REF-1>
and<REF-2>
must match.- If
<REF-1>
is aSinglePin
then<REF-2>
must also be aSinglePin
- If
For the KeyValue
form, the user can use the =>
operator to construct the endpoint
expression <EXP-*>
.
In its simplest form - the KeyValue
expression looks something like this:
structure( <REF-1> => <REF-2> ) = <RoutingStructure|DifferentialRoutingStructure>
The same requirements for <REF-*>
defined in the Explicit Endpoint
form exist
for the KeyValue
form.
Routing Structures
The structure
statement is a means of applying a geometry constraint to a signal topology. Because of the complex nature of a PCB design, this typically requires more than just a single trace width. Hence the need for a "Routing Structure" type that
encodes this information across layers and other board conditions.
RoutingStructure
is the type for the object defined by a pcb-routing-structure statement.DifferentialRoutingStructure
is the type for the object defined by a pcb-differential-routing-structure statement.
Usage
The structure
statement requires the passed endpoints to be part of a Valid Topology.
pcb-differential-routing-structure diff-100:
trace-width = 0.145
pair-spacing = 0.127
clearance = 0.3
...
pcb-component KSZ8081:
port TX : diff-pair
port RX : diff-pair
...
pcb-component RJ45-with-magnetics:
port TX : diff-pair
port RX : diff-pair
...
pcb-module ethernet-demo :
inst PHY : KSZ8081
inst conn : RJ45-with-magnetics
net (PHY.TX, conn.TX)
net (PHY.RX, conn.RX)
; Construct the Topology between the PHY and connector
topology-segment(PHY.TX, conn.TX)
topology-segment(PHY.RX, conn.RX)
; Add the structure constraint for this topology.
structure(PHY.TX => conn.TX) = diff-100
structure(PHY.RX => conn.RX) = diff-100
The above is a typical differential pair example. Here there are two differential pairs, one for TX and one for RX that make up a 100-BaseT lane.
The structure
statement applies a 100 ohm differential impedance to
both diff-pair
ports of the lane.
There are two equivalent forms of this statement.
Equivalent Form #1 - SinglePin
KeyValue Form
; SinglePin KeyValue Form
; Equivalent `structure` statements as the `diff-pair`
structure(PHY.TX.P => conn.TX.P, PHY.TX.N => conn.TX.N ) = diff-100
structure(PHY.RX.P => conn.RX.P, PHY.RX.N => conn.RX.N ) = diff-100
This form is more verbose and explicitly calls out each SinglePin
port
of the diff-pair
. This can be a useful form when the diff-pair signals
are not necessarily in nice, tidy bundles. You may need to select ports
on disparate components and tie them together with a structure
.
Equivalent Form #2 - Exlicit Ref Form
; Explicit Ref Form
; Equivalent `structure` statements as the `diff-pair`
structure(PHY.TX, conn.TX) = diff-100
structure(PHY.RX, conn.RX) = diff-100
In this form, we avoid the =>
operator in favor of an "argument" syntax. This
is an older form of the same constraint.
Single-Ended on PortArray
For certain bus style architectures, like MicroSD, we need to apply a
single-ended routing structure across mutiple singles in a PortArray
.
pcb-routing-structure se-50 :
name = "50 Ohm single-ended"
...
pcb-bundle micro-sd:
port data : pin[4]
port clk : pin
port cmd : pin
pcb-component MCU:
port sd : micro-sd
...
pcb-component SDCard_Connector :
port sd : micro-sd
...
pcb-module portarray-example :
inst mcu : MCU
inst conn : SDCard_Connector
net (mcu.sd.data, conn.sd.data)
topology-segment(mcu.sd.data, conn.sd.data)
structure(mcu.sd.data => conn.sd.data) = se-50
Notice that micro-sd.data
is a PortArray
port consisting of
4 SinglePin
ports. We can connect these two port arrays on the mcu
and conn
instances directly. Subsequently, we can use the structure
statement to apply the routing structure constraint.
In this case, the se-50
routing constraint gets applied, individually,
to each of the signals of the data
port array. This would be the
same as:
for i in 0 to length(mcu.sd.data) do:
structure(mcu.sd.data[i] => conn.sd.data[i]) = se-50
Symbol
The symbol
statement is a means of associating a single-pin "Net Symbol" with a a particular net. This most obvious example is for the ground symbol.
This statement is only valid in the pcb-module
context.
Signature
symbol(<NET>) = <SYMB>
<NET>
- A ref to a single net in the current module's context.<SYMB
- A pcb-symbol definition
Usage
pcb-symbol ground-sym :
pin p[0] at Point(0.0, 0.0)
...
pcb-module top-level:
net GND (MCU.gnd, sensor.gnd)
symbol(GND) = ground-sym
The symbol()
statement in the pcb-module
context allows the user to associate a single pin pcb-symbol
definition with a net.
Here the net GND
is defined by connecting two other components ground ports together. Then
we apply the ground-sym
to that NET with the symbol()
statement.
This results in the creation of this "Net Symbol" in the schematic.
This symbol will be present in the circuit where ever this net is used. In the example of the ground symbol, this will likely have been defined at the top level of the design and then distributed to all sub-modules. This means it will likely be present on all schematic sheets of the design.
If the symbol()
statement is used on a net that is only part of a given sub-module, then that net symbol will only be present within the schematic group of that module.
Not Used on Ports
Notice that the symbol()
statement does not apply to port
objects. If you attempt to apply the symbol()
statement to a port of a module or component, you will encounter an error:
Uncaught Exception: ... Must set a net symbol's net with a single net (received a single pin). in main
Duplicate symbol()
statements
Currently, if you apply the symbol()
statement twice to a given net in a design, then the last applied pcb-symbol
will apply to that net.
timing
The timing
statement is a means of applying a propagation time constraint to a signal topology. The idea is that for certain signals, we may want a particular signal to include a certain propagation delay from transmitter to receiver.
For example, in RGMII v1.3, the CLK
signal requires a 1.5–2 nanosecond delay. The timing
statement provides a way of informing the routing engine of this kind of constraint.
The timing
statement is only valid within the pcb-module
context.
These constraints are applied to the endpoints of a signal topology. The routing engine will infer that this constraint must be applied to every segment of the topology between the passed endpoints.
A timing
statement is dependent on a structure statement to provide the conversion from route geometry to physical delay characteristics.
Signature
; Explicit Endpoint Form
timing(<REF-1>, <REF-2>) = <TimingConstraint>
; KeyValue Form
timing(<EXP>) = <TimingConstraint>
For the "Explicit Endpoint" form, the user provides a ref to a set of ports. The requirements for these ports are:
- The
<REF-*>
must be a ref to a component, module, or abstract (require) port. - Each port must be a
SinglePin
port.
For the "KeyValue" form, the user can use the =>
operator to construct the endpoint expression. Each of the key
and value
ports must be a SinglePin
port.
In its simplest form - the KeyValue
expression looks something like this:
timing( <REF-1> => <REF-2> ) = <TimingConstraint>
The same requirements for <REF-*>
defined in the Explicit Endpoint
form exist for the KeyValue
form.
Timing Constraint
The timing
statement assigns a TimingConstraint
object as a constraint to the topology.
defstruct TimingConstraint <: SignalConstraint :
min-delay:Double
max-delay:Double
defn TimingConstraint (delay: Toleranced) :
...
The min-delay
and max-delay
are relative delays limits in units of seconds.
The delay per unit length of a signal's route is determined by the Routing Structure applied via the structure statement. Without a structure
statement that applies to the underlying topology.
Usage
Below is an example of using the timing
constraint for one lane of an RGMII interface:
pcb-routing-structure se-50:
...
pcb-bundle rgmii-lane :
port data : pin[4]
port clk : pin
port ctl : pin
pcb-component MCU:
TX : rgmii-lane
...
pcb-component PHY:
RX : rgmii-lane
...
pcb-module rgmii-example:
inst mcu : MCU
inst phy : PHY
topo-net(mcu.TX => phy.RX)
structure(mcu.TX.data => phy.RX.data) = se-50
structure(mcu.TX.clk => phy.RX.clk) = se-50
structure(mcu.TX.ctl => phy.RX.ctl) = se-50
...
val clk-delay = min-max(1.5e-9, 2.0e-9)
timing(mcu.TX.clk => phy.RX.clk) = TimingConstraint(clk-delay)
Notes that this example uses the topo-net to construct the topology.
The timing
statement above is equivalent to the following form:
timing(mcu.TX.clk, mcu.RX.clk) = TimingConstraint(clk-delay)
timing-difference
The timing-difference
statement is a means of applying a timing skew constraint between two signal topologies. A typical application is "length matching" the P/N signals of a differential pair. This constraint is used by the routing engine to apply the appropriate skew matching routes (lovingly referred to as "squiggles").
The skew between two signal topologies is formulated as a time difference instead of a length difference because different layers, routing structures, and layers may have different propagation delays associated with them.
This statement is only valid within the pcb-module
context.
This constraints are applied to the endpoints of a signal topology. The routing engine will infer that this constraint must be applied to every segment of the topology between the passed endpoints.
Signature
timing-difference(<EXP-1>, <EXP-2>) = <TimingDifferenceConstraint>
The timing-difference
statement expects two expressions that outline the endpoints of two signal topologies. Each of these
expressions is expected to be a KeyValue
representing the
two endpoints of each signal topology.
The simplest form of this statement looks something like this:
timing-difference( <REF-A-1> => <REF-A-2>, <REF-B-1> => <REF-B-2>) = <TimingDifferenceConstraint>
Here each of the <REF-*-*>
are refs to SinglePin
port of a component, module, or abstract (require) port.
In most applications, the endpoints are the extrema of a signal topology.
Constructing cycles in these constraints is disallowed. This means that <REF-A-1>
cannot be the same port as <REF-A-2>
.
TimingDifferenceConstraint
An instance of TimingDifferenceConstraint
is applied to the timing-difference
statement:
defstruct TimingDifferenceConstraint <: SignalConstraint :
min-delta:Double
max-delta:Double
defn TimingDifferenceConstraint (delta: Toleranced) :
...
The min-delta
and max-delta
are relative skew limits in units of seconds between the two signal routes to be constrained. max-delta
must be greater than min-delta
.
It is typical in most applications for min-delta
to be negative and max-delta
to be positive.
The delay introduced by a particular signal route is determined by the Routing Structure applied via the structure statement.
Usage
The most common application is for intra differential pair skew, but this can also be used to apply interpair or bus skew constraints:
pcb-differential-routing-structure diff-100:
name = "100 Ohm Diff Impedance"
...
pcb-bundle MIPI-CSI (lane-cnt:Int):
port clk : diff-pair
port data : diff-pair[lane-cnt]
pcb-component CMOSImageArray :
port mipi : MIPI-CSI(1)
...
pcb-component MCU :
port mipi : MIPI-CSI(1)
pcb-module camera:
inst mcu : MCU
inst imager : CMOSImageArray
topo-net(mcu.mipi => imager.mipi)
structure(mcu.mipi.clk => imager.mipi.clk) = diff-100
for i in 0 to length(mcu.mipi.data) do:
structure(mcu.mipi.data[i] => imager.mipi.data[i]) = diff-100
; Intra-Pair Skew - P / N
val intra-skew = TimingDifferenceConstraint(0.0 +/- 10.0e-12)
timing-difference(
mcu.mipi.clk.P => imager.mipi.clk.P,
mcu.mipi.clk.N => imager.mipi.clk.N,
) = intra-skew
for i in 0 to length(mcu.mipi.data) do:
timing-difference(
mcu.mipi.data[i].P => imager.mipi.data[i].P,
mcu.mipi.data[i].N => imager.mipi.data[i].N,
) = intra-skew
; Inter Pair Skew - Clk to Data
val inter-skew = TimingDifferenceConstraint(0.0 +/- 25.0e-12)
timing-difference(
mcu.mipi.clk.P => mcu.mipi.clk.P,
mcu.mipi.data[0].P => mcu.mipi.data[0].P
) = inter-skew
In this example, we introduce both a intra-pair skew constraint for each of the differential pairs of the MIPI interface, as well as a inter-pair skew constraint between the clock and data differential pairs.
Notice that the inter-pair constraint takes advantage of existing intra-pair constraint to minimize the number of constraints that must be added to routing engine. The least number of constraints will result in the best routing engine performance. This goal of least number of constraints comes at a cost. This structure assumes that the intra-pair skew tolerance is less than the inter-pair skew tolerance.
If we do a tolerance stackup analysis, we will see that this structure results in a case where the clk.N
and the data[0].N
may have up to 70 pS of skew.
In your application, this may not be acceptable. In that case, you may need to add additional constraints. Bear in mind that each constraint adds to the complexity of the problem the routing engine must solve.
topology-segment
The topology-segment
statement is a means of constructing the ordered edges of a signal topology graph. The topology is underlying scaffold that the routing engine uses to apply various physical constraints on each signal.
The edges of the topology graph are made up of topology "segments" and pin-model statements. A segment connects two endpoints. Each endpoint must be a component, module, or abstract port.
The topology-segment
statement is only valid within the pcb-module
context.
Signature
topology-segment(<EXP-1, EXP-2>)
Notes:
- The
<EXP-*>
is expected to be a ref of a component, module, or abstract port. - That port can be either a
SinglePin
,Bundle
, orPortArray
. - The ports referenced in
<EXP-1>
and<EXP-2>
should not be the same port. This would construct an immediate cycle. - The ports
<EXP-1>
and<EXP-2>
must match in type. Mismatching ports will elicit an exception from the JITX runtime. - The ports
<EXP-1>
and<EXP-2>
must be connected to the same net
Usage
The topology-segment
statement is typically the first step in constructing a signal integrity route. A valid topology is required for any constraints like
structure, insertion-loss, etc to apply correctly. As such, you will often see the topology constructed first and
then constraints applied afterward.
pcb-differential-routing-structure diff-100:
trace-width = 0.145
pair-spacing = 0.127
clearance = 0.3
...
pcb-component KSZ8081:
port TX : diff-pair
port RX : diff-pair
...
pcb-component RJ45-with-magnetics:
port TX : diff-pair
port RX : diff-pair
...
pcb-component esd-protector :
port ch : dual-pair[4]
...
pin-model(self.ch[0].A, self.ch[0].B) = PinModel(typ(0.0), typ(0.0))
pin-model(self.ch[1].A, self.ch[1].B) = PinModel(typ(0.0), typ(0.0))
...
pcb-module ethernet-demo :
inst PHY : KSZ8081
inst conn : RJ45-with-magnetics
inst ESD : esd-protector
net (PHY.TX, conn.TX)
net (PHY.RX, conn.RX)
; Construct the Topology between the PHY and connector
; There are two hops
; PHY => ESD => Connector
topology-segment(PHY.TX, ESD.ch[0].A)
topology-segment(ESD.ch[0].B, conn.TX)
topology-segment(PHY.RX, ESD.ch[1].A)
topology-segment(ESD.ch[1].B, conn.RX)
; Add the structure constraint for this topology.
structure(PHY.TX => conn.TX) = diff-100
structure(PHY.RX => conn.RX) = diff-100
Here the topology-segment
statements are acting on diff-pair bundles. This
is a common structure when applying differential pair constraints.
Notice that the topology segments are point-to-point. We connect the PHY to one side of the ESD protector, and then from the other side of the ESD protector to the connector.
After the construction of the topology, we can apply constraints. Notice that the structure
constraint is applied to the endpoints (eg PHY.TX
and conn.TX
) and does not need to consider the interstitial nodes between the endpoints. The topology definition provides this information to the routing engine without the user needing to specify it directly.
The pin-model
statements on the esd-protector
component form a critical part of this infered topology. Without the pin-model
, the routing engine would not know that the topology segment on the PHY side and the topology segment on the connector side are connected.
References:
- The dual-pair is a bundle type defined in JSL for series connecting differential pairs.
- The pin-model is a statement for components that creates a topology edge between the ports of the ESD protector.
Helper Routines in JSL
In JSL, there are some helper routines to make the construction of topology-segment
statements easier.
See:
- topo-net - This is a utility function for creating a
net
andtopology-segment
statement simultaneously. - topo-pair - Utility function for constructing differential pair topologies with interstitial components.
value-label
The value-label
statement is used to override or set the value parameter of a component instance that maps to the >VALUE
template.
This statement is only valid from within the pcb-module
context.
Signature
value-label(<INST>) = <Text>
<INST>
- A ref to an instance of a component in the currentpcb-module
context.<Text>
- AText
shape instance that describes the features of the value label for this component instance.- The
Text
object will define the location, pose, size, and font for the value label. - The string content of the text can be any value without restriction.
- The
Usage
The >VALUE
template string can be applied in pcb-symbol
and pcb-landpattern
layer statements with the Text
object. This is a convenient template string to use to show a resistor's value in the schematic.
The value-label
can override the substituted string used for the >VALUE
template.
; name the test point,
value-label(my-component) = Text("my-component-value", 1.0, W, loc(1.2, 0.0))
This new Shape
(of type Text
) will be used to:
- Override any existing value parameter as defined in the
mpn
oremodel
statements. - In the
pcb-symbol
of this component instance:- If there exists one or more
layer(...) = Text(">VALUE" ...)
statements - then the passedText
object's string value (in this casemy-component-value
) will replace the>VALUE
of those text objects and leave the size, anchor, and pose parameters will remain unchanged.
- If there exists one or more
- In the
pcb-landpattern
of this component instance:- It constructs a new
layer(Silkscreen("f-silk", Top)) = ...
statement with the suppliedText
object. - All
Text
objects on any valid layer will be inspected for a>VALUE
content string. For all of those instances - only the string replacement will occur. In the case above the>VALUE
will becomemy-component-value
and the size, anchor, and pose parameters will remain unchanged.
- It constructs a new
variant
The variant
statement provides the flexibility for the design to have variations on the components used. Each variant
is identified with its name. The apply-variants
command takes a list of variant names and applies them to create the variation specified for the design.
Syntax
pcb-module my-module :
port power
inst dnp-me : my-component
inst my-res : my-resistor(1000.0)
; Change component status
variant "DNP" :
; Change component status
do-not-populate(dnp-me)
instance-status(dnp-me) :
board-status = NotOnBoard
; Switch the component of an instance
variant "High Resistance" :
component(my-res) = my-resistor(1.0e06)
; Change property "voltage" of a pin
variant "High Power" :
state(power.voltage) = "high"
; Change property "rated-temperature" of the instance my-inst
variant "TOUGH Fuse" :
property(my-inst.rated-temperature) = min-max(-40.0 85.0)
; Apply Variants
val variation = apply-variants(["DNP" "High Power" "High Resistance"] my-module)
set-main-module(variation)
Description
variant "variant name" :
Specifies a Variant that may alter the inst in this module.
Its application is determined by a command apply-variants
later in top level.
For example, if later on, a top level command
val new-module = apply-variants(["DNP" "High Power"] my-module)
is executed, the inst
dnp-me
will become do-not-populate
and power
pin will have 5V in new-module
.
The following statements are supported within a variant
statement:
instance-status
anddo-not-populate
statements modify the on-board and BOM statuses of components.state
andproperty
statements modify the property/state of an instancecomponent
statement replaces the component of an instance by another component, provided they have the same landpattern.
Example Code
#use-added-syntax(jitx)
defpackage my-design :
import core
import jitx
import jitx/commands
import ocdb/utils/defaults
import ocdb/utils/landpatterns
; smd-pad, also needs default rules in ocdb/utils/defaults
pcb-component my-component :
name = "my component"
property(self.rated-temperature) = min-max(-55.0 125.0)
pcb-landpattern my-landpattern :
pad a : smd-pad(Circle(1.0)) at loc(0.0, 0.0) on Top
;pad a : square-pad at loc(0.0, 0.0) on Top
layer(SolderMask(Top)) = Rectangle(1.25, 1.25)
pcb-component my-resistor (resistance:Double):
emodel = Resistor(resistance)
assign-landpattern(my-landpattern)
pcb-module main:
port gnd
port power
port signal
; Change component status
public inst my-inst : my-component
variant "DNP" :
do-not-populate(my-inst)
instance-status(my-inst) :
board-status = NotOnBoard
; Switch the component of an instance to another one with the same landpattern
public inst my-res : my-resistor(10.0e3)
variant "High Resistance":
component(my-res) = my-resistor(10.0e6)
; Change property "voltage" of the pin power
property(power.voltage) = Stateful(["low" => 1.8, "medium" => 3.3, "high" => 5.0])
state(power.voltage) = "low"
variant "High Power" :
state(power.voltage) = "high"
variant "Medium Power" :
state(power.voltage) = "medium"
; Change property "rated-temperature" of the instance fuse
variant "TOUGH Fuse" :
property(my-inst.rated-temperature) = min-max(-40.0 85.0)
; Apply Variants
val variation = apply-variants(["DNP" "High Power" "High Resistance"] main)
set-main-module(variation)
; Verify the variants are properly applied
println $ do-not-populate?(variation.my-inst)
println $ state(variation.power.voltage)
println $ emodel?(variation.my-res)
The output of the above code would be
true
high
Resistor(10000000.0)
Pads
A pcb-pad
definition represents a single lead found on a pcb-landpattern
.
Syntax
pcb-pad my-pad :
type = SMD ; or TH
shape = Rectangle(1.0, 0.5)
layer(SolderMask(Top)) = Rectangle(1.25, 0.75)
Statements
Statement | Description |
---|---|
type | The type of the pad, either SMD (surface mount) or TH (through hole) |
shape | The shape of the pad. |
name | Name |
description | Description |
layer | Layers on the pad, like solder mask and cutouts. |
Name
The name
statement is the optional name field of a JITX object. Use it to store a descriptive name as a String
. This name will often be used in the UI in place of the object's expression name for better readability.
Signature
name = <String>
This statement is optional. The default value will be the definition's symbol name in case no name
statement is found in a definition.
Any string is a valid name
value.
Usage
Literal String Names
pcb-component component :
name = "ADM7150"
pcb-module band-pass-filter :
name = "Band-pass filter"
The examples name = "ADM7150"
and name = "Band-pass filter"
use a String
literal for the name.
Formatted Strings
pcb-pad smd-pad (anchor:Anchor, w:Double, h:Double) :
name = to-string("%_x%_ %_ SMD Pad" % [w,h,anchor])
pcb-landpattern test-lp:
pad p[1] : smd-pad(C, 0.6, 0.7) at loc(x0,y0)
We can also construct strings using formatting routes and parameter arguments for a particular definition. In this example, the constructed smd-pad
name property would be 0.6x0.7 C SMD Pad
Description
The description
statement is the optional descriptive field of a JITX object. Use it to store a description of the object for human designers reading JITX, and to make the object easier to find via text search. This description also shows up in the UI, such as the design explorer, to provide more insight into particular components and modules in the design.
Signature
description = <String|False>
This statement is optional. The default value will be false
in case no description
statement is found in a definition.
Any string is a valid description
value.
Each JITX definition may have exactly one description
statement. If more than one statement is encountered in a definition, then a DuplicateCStmtError
exception will be raised.
Examples
; We can use string literals to describe a particular component.
pcb-component analog-devices-ADM7150 :
description = "800 mA Ultralow Noise, High PSRR, RF Linear Regulator"
; We can use string formatting to construct descriptions based on variables
; or arguments to a JITX Definition
pcb-module band-pass-filter (high-cut:Double, low-cut:Double) :
description = to-string(
"Band-pass Filter - Highpass = %_ Hz and Lowpass = %_ Hz." % [high-cut, low-cut]
)
Layer
The layer
statement is used to create geometry on the non-copper layers of a circuit board. The layer()
statement is valid in the following contexts:
Signature
layer(<LayerSpecifier>) = <Shape>
<LayerSpecifier>
- A LayerSpecifier instance that identifies which non-copper layer to apply the provided geometry to.<Shape>
- AShape
instance that defines the geometry that will be created on the specified layer.
Usage
The most common usage of the layer()
statement is in pcb-landpattern:
pcb-landpattern diode-lp :
pad c : smd-pad(0.4, 0.75) at loc(-1.25, 0.0) on Top
pad a : smd-pad(0.4, 0.75) at loc(1.25, 0.0) on Top
layer(Silkscreen("body", Top)) = LineRectangle(1.8, 1.0)
layer(Silkscreen("body", Top)) = Line(0.1, [Point(-0.70, -0.5), Point(-0.70, 0.5)])
layer(Courtyard(Top)) = Rectangle(3.2, 2.0)
layer(ForbidCopper(LayerIndex(0))) = Rectangle(2.0, 1.0)
This will construct a landpattern that looks like this:
Notice the silkscreen in yellow with cathode marker. The blue box is the ForbidCopper
layer on the Top Layer. Red is the top copper pads for the cathode c
and anode a
.
The white bounding rectangle is the Courtyard
layer.
See LayerSpecifier for more information about specific layers.
Cutouts
When constructing cutouts in the board layout, your best bet is to use a solid region as opposed to a line. A line can confuse the routing engine into thinking that there are two physically separate regions where copper can be placed.
Consider a USB connector part, U231-096N-3BLRT06-SS, Jing Extension of the Electronic Co.
Here is an excerpt from the datasheet:
If we draw the cutout with a line, as shown in the datasheet, we get this:
pcb-landpattern USB-conn:
...
layer(Cutout()) = Line(0.254, [Point(-7.300, -7.650), Point(-8.450, -7.650)])
layer(Cutout()) = Polyline(0.254, [
Point(6.850, 4.650)
Point(6.850, -7.650)
Point(8.450, -7.650)])
layer(Cutout()) = Polyline(0.254, [
Point(-6.850, 4.650)
Point(-6.850, -7.650)
Point(-7.300, -7.650)])
layer(Cutout()) = Line(0.254, [Point(-6.850, 4.650), Point(6.850, 4.650)])
The cutout line is in gold color. Notice that the ground layer (blue) copper is present on both sides of the cut line with some margin in between. The routing engine basically thinks that the cutout is just the line. If we were making a slot - that would probably be reasonable. But for this case, we want the hole region between the cutout line and the board edge (red) to be a cutout region.
The right way is to use a Rectangle
or Polygon
solid geometry:
pcb-landpattern USB-conn :
...
layer(Cutout()) = Polygon([
Point(-6.850, 4.650), Point(6.850, 4.650),
Point(6.850, -7.650), Point(-6.850, -7.65),
])
layer(Cutout()) = Circle(Point(-6.85 + 0.5, 4.65), 0.5)
layer(Cutout()) = Circle(Point(6.85 - 0.5, 4.65), 0.5)
Notice that the cutout region fills the entire connector region and the blue ground plane is not present in the cutout region.
Rules
A pcb-rules
definition represents the set of design rules associated with a specific circuit board manufacturer.
Syntax
pcb-rules bay-area-circuits-std-rules :
min-copper-width = 0.127
min-copper-copper-space = 0.127
min-copper-hole-space = 0.2032
min-copper-edge-space = 0.381
min-annular-ring = 0.1524
min-drill-diameter = 0.254
min-silkscreen-width = 0.0762
min-pitch-leaded = 0.35
min-pitch-bga = 0.35
max-board-width = 457.2
max-board-height = 609.6
min-silk-solder-mask-space = 0.127
min-silkscreen-text-height = 0.75
solder-mask-registration = 0.106
min-th-pad-expand-outer = 0.2032
min-soldermask-opening = 0.152
min-soldermask-bridge = 0.102
min-hole-to-hole = 0.254
min-pth-pin-solder-clearance = 3.0
Description
All design rules are required to have a value. The supported design rules are :
Rule Statement | Description |
---|---|
min-copper-width | Minimum size of a copper feature on a geom . |
min-copper-copper-space | Minimum distance between copper features on the same layer. |
min-copper-hole-space | Minimum distance from a hole or cutout feature to a copper feature on any layer. |
min-copper-edge-space | Minimum distance from the edge of the board to a copper feature on any layer. |
min-annular-ring | Minimum size of the annular ring around a hole or via. |
min-drill-diameter | Minimum diameter of a hole, either in a pad or via. |
min-silkscreen-width | Minimum size of a silkscreen feature. |
min-pitch-leaded | Minimum distance between pad centers of a leaded component. |
min-pitch-bga | Minimum distance between pad centers of a BGA component. |
max-board-width | Maximum size of a board in the x direction. |
max-board-height | Maximum size of a board in the y direction. |
min-silk-solder-mask-space | Minimum distance between silkscreen and solder mask features. |
min-silkscreen-text-height | Minimum height of text on silkscreen layers. |
min-pth-pin-solder-clearance | Minimum distance between a through hole pad and solder mask. |
solder-mask-registration | Minimum distance from the edge of a copper pad and solder mask feature. |
min-soldermask-opening | Minimum size of a solder mask shape. |
min-soldermask-bridge | Minimum distance between solder mask features. |
min-hole-to-hole | Minimum distance between two holes in pads or vias. |
min-th-pad-expand-outer | Minimum distance from the outer edge of a PTH pad's annular ring to any copper feature. |
Statements
In addition to the set of supported design rules, pcb-rules
may have some additional, optional statements.
Statement | Description |
---|---|
description | Description |
name | Name |
Name
The name
statement is the optional name field of a JITX object. Use it to store a descriptive name as a String
. This name will often be used in the UI in place of the object's expression name for better readability.
Signature
name = <String>
This statement is optional. The default value will be the definition's symbol name in case no name
statement is found in a definition.
Any string is a valid name
value.
Usage
Literal String Names
pcb-component component :
name = "ADM7150"
pcb-module band-pass-filter :
name = "Band-pass filter"
The examples name = "ADM7150"
and name = "Band-pass filter"
use a String
literal for the name.
Formatted Strings
pcb-pad smd-pad (anchor:Anchor, w:Double, h:Double) :
name = to-string("%_x%_ %_ SMD Pad" % [w,h,anchor])
pcb-landpattern test-lp:
pad p[1] : smd-pad(C, 0.6, 0.7) at loc(x0,y0)
We can also construct strings using formatting routes and parameter arguments for a particular definition. In this example, the constructed smd-pad
name property would be 0.6x0.7 C SMD Pad
Description
The description
statement is the optional descriptive field of a JITX object. Use it to store a description of the object for human designers reading JITX, and to make the object easier to find via text search. This description also shows up in the UI, such as the design explorer, to provide more insight into particular components and modules in the design.
Signature
description = <String|False>
This statement is optional. The default value will be false
in case no description
statement is found in a definition.
Any string is a valid description
value.
Each JITX definition may have exactly one description
statement. If more than one statement is encountered in a definition, then a DuplicateCStmtError
exception will be raised.
Examples
; We can use string literals to describe a particular component.
pcb-component analog-devices-ADM7150 :
description = "800 mA Ultralow Noise, High PSRR, RF Linear Regulator"
; We can use string formatting to construct descriptions based on variables
; or arguments to a JITX Definition
pcb-module band-pass-filter (high-cut:Double, low-cut:Double) :
description = to-string(
"Band-pass Filter - Highpass = %_ Hz and Lowpass = %_ Hz." % [high-cut, low-cut]
)
Routing Structures
Routing structures are definitions that specify geometric constraints on nets and topologies.
Statement | Description |
---|---|
pcb-routing-structure | Routing Structures for Single-Ended Topologies |
pcb-differential-routing-structure | Routing Structures for Differential Topologies |
pcb-routing-structure
The pcb-routing-structure
is a statement for defining the geometric constraints of a single-ended topology. This may include:
- Singled-ended transmission lines, like microstrip and striplines.
- Uncoupled regions for differential pair transmission lines.
- Fanout regions for BGAs or other dense components.
Outline
pcb-routing-structure struct-name (arg1:Type1, ...) :
name = <String>
description = <String>
layer-constraints(<LayerIndex|Side>):
trace-width = <Double> ; mm
clearance = <Double> ; mm
velocity = <Double> ; mm/s
insertion-loss = <Double> ; dB/mm
; Option #1 - NeckDown Instance
neck-down = <NeckDown>
; Option #2 - NeckDown Macro
; Notice the `:` instead of `=`
neck-down :
trace-width = <Double>
clearance = <Double> ; mm
velocity = <Double> ; mm/s
insertion-loss = <Double> ; dB/mm
layer-constraints(<LayerIndex|Side>):
...
The routing structure requires an expression name struct-name
that uniquely identifies this statement within the current context.
The arguments list (arg1:Type1, ...)
is optional and provides a means to parameterize routing structures for common statements.
The name
and description
properties are optional and are primarily for documentation and labeling purposes. The name
property in particular is typically for UI applications. If a name
property is not provided, then the expression name struct-name
will be used.
The elements of the pcb-routing-structure
statement are identified using a layer-constraints
statement. Each of these layer statements must match with a conductor layer in the pcb-stackup defined for the current board design. Conceptually, the idea is to apply different properties depending on the layer.
The layer-constraits()
statement takes an argument of either a Side
like Top
or Bottom
, or a LayerIndex
. See LayerIndex for more information.
At each layer specification, the user can list any of these properties:
- Required Properties:
trace-width
- Width in mm of the traces on this layer.velocity
- Signal Propagation Velocity (also known as Group Velocity) of the signals on this layer. This is primarily used for timing constraints. This property is in unitsmm/s
.insertion-loss
- Insertion Loss per unit distance of the signals on this layer.- This property is in units of
dB/mm
- This property is assumed to be at the frequency of interest for this design.
- This property is in units of
- Optional Properties
clearance
- Minimum "Net to Net" clearance in mm of the traces on this layer. If not provided, then the default minimum clearances from the design rules will be used.neck-down
- This property allows the user to specify a special set of routing properties for the neckdown region. It comes in two forms:- Instance Form - the user must assign a NeckDown instance.
- Macro Form - the user can provide, directly, statements for
trace-width
,clearance
, etc like above.
If these properties are not explicitly provided in the routing structure, then either:
- The default design rules for the board will be consulted for
trace-width
andclearance
- If there are timing or loss constraints on this route, but no defined structure information for layers that have traces, then default values will be used for
velocity
andinsertion-loss
. The current default values are:velocity = 0.15e12 ; mm/s
insertion-loss = 0.002 ; dB/mm
Usage
Here are a list of common examples:
Microstrip Example
val eps-r = 4.6
val vel = phase-velocity(eps-r)
val target-imped = 50.0
val thickness = 0.035 ; mm
val height = 0.1; mm
; Compute an estimate of the trace width
; for a 50 ohm impedance.
val w = ipc2141-microstrip-width(
target-imped, eps-r, thickness, height
)
public pcb-routing-structure se-50 :
name = "50 Ohm Microstrip"
layer-constraints(Top) :
trace-width = w
clearance = w * 3.0
velocity = vel
insertion-loss = 0.008
layer-constraints(Bottom) :
trace-width = w
clearance = w * 3.0
velocity = vel
insertion-loss = 0.008
In this example, we construct a microstrip transmission line for the Top
and Bottom
layers.
Caution!
This routing structures does not provide parameters for any of the internal layers. If any of the routes that have this routing structure assigned to them transition to an internal layer, then the runtime will consult the default design rules for the trace width constraint.
Using NeckDown
; Define a single-ending routing structure for the
; uncoupled regions
public pcb-routing-structure se-100 ( -- clearance-mult:Double = 3.0 ) :
name = "100 Ohm Microstrip"
val w-100 = 0.1048
layer-constraints(Top) :
trace-width = w-100
clearance = w-100 * clearance-mult
velocity = 0.19e12
insertion-loss = 0.008
neck-down = NeckDown(
clearance = 0.5 * clearance-mult * w-100
)
...
; Application
structure(A => B) = se-100( clearance-mult = 2.5 )
Here the neck-down
property is used to change the clearance properties of neckdown traces on the Top
layer.
Reusing Layer-Constraints Definitions
Sometimes, we would like to construct repeated layer-constraints
definitions
in the internal layers of a board, without resorting to copy/pasting code. We
have two methods for doing this.
Using RoutingStructureLayerConstraints
val my-layer-constraints = RoutingStructureLayerConstraints(
trace-width = 0.27
clearance = 0.37
velocity = 0.17e12
insertion-loss = 0.0087
neck-down = NeckDown(
trace-width = 0.1567
clearance = 0.17
velocity = 0.17e12
insertion-loss = 0.0087
)
)
pcb-routing-structure my-routing-structure :
name = "50 Ohm Single Ended"
layer-constraints(Top) = my-layer-constraints
layer-constraints(Bottom) = my-layer-constraints
Notice the syntax: after layer-constraints(Top)
we use an equals sign rather
than colon. This indicates the use of a pre-defined
RoutingStructureLayerConstraints
object. In place of my-layer-constraints
after the equals sign, we could alternately use a function call or any Stanza
expression that returns an object of this type.
Note that the definition of my-layer-constraints
here is pure Stanza code,
with no special macros. So when specifying neck-down we have to use the
ordinary constructor for NeckDown
(Option #1 above), not the NeckDown macro.
Using Generator Functions
We use the inside pcb-routing-structure
macro to define a generator that can
be called inside the pcb-routing-structure
definition.
; Generator for constructing a stripline at 75 ohm
; on a target internal layer.
defn gen-internal-stripline (l:LayerIndex) :
val w = 0.12
inside pcb-routing-structure:
layer-constraints(l):
trace-width = w
clearance = 3.0 * w
velocity = 0.19e12 ; mm / s
insertion-loss = 0.008 ; dB / mm
pcb-routing-structure se-75:
name = "75 ohm Single-Ended"
val ms-w = 0.095
layer-constraints(Top):
trace-width = ms-w
clearance = 3.0 * ms-w
velocity = 0.19e12
insertion-loss = 0.008
for sig-layer in [2, 5, 7] do:
gen-internal-stripline(LayerIndex(sig-layer))
layer-constraints(Bottom):
trace-width = ms-w
clearance = 3.0 * ms-w
velocity = 0.19e12
insertion-loss = 0.008
In this example, we use a generator function to construct the layer-constraints()
statement for the internal layers. We then use a for-loop
to construct that same structure multiple times.
The resulting pcb-routing-structure
object has the following layer statements:
layer-constraints(Top)
withtrace-width = 0.095
layer-constraints(LayerIndex(2))
withtrace-width = 0.12
layer-constraints(LayerIndex(5))
withtrace-width = 0.12
layer-constraints(LayerIndex(7))
withtrace-width = 0.12
layer-constraints(Bottom)
withtrace-width = 0.095
pcb-differential-routing-structure
The pcb-differential-routing-structure
is a statement for defining the geometric constraints of a differential pair topology. This definition applies to only the coupled region of a differential pair.
Outline
pcb-differential-routing-structure struct-name (arg1:Type1, ...) :
name = <String>
description = <String>
uncoupled-region = <RoutingStructure>
layer-constraints(<LayerIndex|Side>):
trace-width = <Double> ; mm
pair-spacing = <Double> ; mm
clearance = <Double> ; mm
velocity = <Double> ; mm/s
insertion-loss = <Double> ; dB/mm
; Option #1 - DifferentialNeckDown Instance
neck-down = <DifferentialNeckDown>
; Option #2 - Macro Form
; Notice the `:` instead of `=`
neck-down:
trace-width = <Double>
pair-spacing = <Double> ; mm
clearance = <Double> ; mm
velocity = <Double> ; mm/s
insertion-loss = <Double> ; dB/mm
layer-constraints(<LayerIndex|Side>):
...
The routing structure requires an expression name struct-name
that uniquely identifies this statement within the current context.
The arguments list (arg1:Type1, ...)
is optional and provides a means to parameterize routing structures for common statements.
The name
and description
properties are optional and are primarily for documentation and labeling purposes. The name
property in particular is typically for UI applications. If a name
property is not provided, then the expression name struct-name
will be used.
The uncoupled-region
property is used to apply a particular single-ended routing structure (ie, a pcb-routing-structure) to the uncoupled regions of a differential pair. This typically applies to via breakouts, fanout from an IC, etc.
The elements of the pcb-differential-routing-structure
statement are identified using a layer
statement. Each of these layer statements must match with a conductor layer in the pcb-stackup defined for the current board design. Conceptually, the idea is to apply different properties depending on the layer.
The layer-constraints()
statement takes an argument of either a Side
like Top
or Bottom
, or a LayerIndex
. See LayerIndex for more information.
At each layer specification, the user can list any of these properties:
- Required Properties:
trace-width
- Width in mm of both of the diff-pair conductors for any traces on this layer.pair-spacing
- Spacing distance between the conductors of the differential pair in mm.velocity
- Signal Propagation Velocity (also known as Group Velocity) of the signals on this layer. This is primarily used for timing constraints. This property is in unitsmm/s
.insertion-loss
- Insertion Loss per unit distance of the signals on this layer.- This property is in units of
dB/mm
. - This property is assumed to be at the frequency of interest for this design.
- This property is in units of
- Optional Properties
clearance
- Minimum "Net to Net" clearance in mm of the traces on this layer. If not provided, then the default minimum clearances from the design rules will be used. This clearance does not affect thepair-spacing
for the differential pair.neck-down
- This property allows the user to specify a special set of routing properties for the neckdown region. It comes in two forms:- Instance Form - the user must assign a DifferentialNeckDown instance.
- Macro Form - the user can provide, directly, statements for
trace-width
,pair-spacing
,clearance
, etc like above.
If these properties are not explicitly provided in the routing structure, then either:
- The default design rules for the board will be consulted for
trace-width
andclearance
- If there are timing or loss constraints on this route, but no defined structure information for layers that have traces, then default values will be used for
velocity
andinsertion-loss
. The current default values are:velocity = 0.15e12 ; mm/s
insertion-loss = 0.002 ; dB/mm
Usage
Below are some typical examples of differential routing structures in action.
Edge Coupled Microstrip
; Define a single-ending routing structure for the
; uncoupled regions
public pcb-routing-structure se-50 ( w:Double = 0.1048 ) :
name = "50 Ohm Microstrip"
layer-constraints(Top) :
trace-width = w
clearance = w * 3.0
velocity = 0.19e12
insertion-loss = 0.008
layer-constraints(Bottom) :
trace-width = w
clearance = w * 3.0
velocity = 0.19e12
insertion-loss = 0.008
val ec-stripline-ph-vel = 0.19e12 ; mm / s @ 1GHz
val ec-stripline-loss = 0.008 ; dB/mm @ 1GHz
public pcb-differential-routing-structure diff-100 :
name = "100 Ohm differential impedance"
uncoupled-region = se-50()
layer-constraints(Top) :
trace-width = 0.0762 ; mm - 3mil
pair-spacing = 0.127 ; mm - 5mil
clearance = 0.3 ; mm
velocity = ec-stripline-ph-vel ; mm/s
insertion-loss = ec-stripline-loss ; dB/mm @ 1GHz
layer-constraints(Bottom) :
trace-width = 0.0762 ; mm - 3mil
pair-spacing = 0.127 ; mm - 5mil
clearance = 0.3 ; mm
velocity = ec-stripline-ph-vel ; mm/s
insertion-loss = ec-stripline-loss ; dB/mm @ 1GHz
In this example, we define two routing structures - a singled ended 50 ohm version and a differential 100 ohm impedance version. The single-ended routing structure is used for the uncoupled regions of the differential pair due to the uncoupled-region
statement.
In this example, We've exaggerated the single-ended uncoupled region to make it more visible. The "Insertion Point" is a control point that the user can place to create the transition from the uncoupled regions to the coupled differential pair regions.
Reusing Layer-Constraints Definitions
As we discussed in the single-ended case, we can reuse
the layer-constraints definitions inside a pcb-differential-routing-structure
.
In this case we need to define a DiffRoutingStructureLayerConstraints
object:
val my-diff-layer-constraints = DiffRoutingStructureLayerConstraints(
trace-width = 0.18
pair-spacing = 0.158
clearance = 0.38
velocity = 0.18e12
insertion-loss = 0.0088
neck-down = DifferentialNeckDown(
pair-spacing = 0.148
trace-width = 0.17
clearance = 0.28
)
)
pcb-differential-routing-structure my-differential-routing-structure :
name = "100 Ohm common-mode impedance"
layer-constraints(Top) = my-diff-layer-constraints
layer-constraints(Bottom) = my-diff-layer-constraints
uncoupled-region = my-routing-structure
The generator function method also works, using inside pcb-differential-routing-structure
in place of inside pcb-routing-structure
.
NeckDown
There are two types for describing the neck down region of a trace:
NeckDown
- This targets Single-Ended tracesDifferentialNeckDown
- This targets Differential Pair traces
Outline - NeckDown
defstruct NeckDown :
trace-width: Double|False
clearance: Double|False
insertion-loss: Double|False
velocity: Double|False
defn NeckDown (-- trace-width: Double|False = false,
clearance: Double|False = false,
insertion-loss: Double|False = false,
velocity: Double|False = false) -> NeckDown :
...
All properties of the NeckDown
object are optional. If a property is
not provided in the instance, then the pcb-routing-structure
property for the current layer will be used by default. These properties act as overrides of the routing structure properties in the neckdown region of a trace.
trace-width
- Width of the trace in mmclearance
- Minimum "Net to Net" clearance in mm of the tracesvelocity
- Signal Propagation Velocity (also known as Group Velocity) of the signals in the neckdown region. This is primarily used for timing constraints. This property is in unitsmm/s
.insertion-loss
- Insertion Loss per unit distance of the signals in the neckdown region. This property is in units ofdB/mm
Usage - NeckDown
pcb-routing-structure se-50 :
layer-constraints(Top):
trace-width = 0.12
clearance = 0.2
velocity = 0.19e12
insertion-loss = 0.008
neck-down = NeckDown(
width = 0.085
clearance = 0.15
insertion-loss = 0.012
)
In this example - the neckdown region overrides the width
, clearance
, and insertion-loss
but leaves the velocity
property with the default 0.19e12
value.
Creating a Neckdown Region
To activate the neckdown region, we need to convert a section of trace to the "Neckdown" type:
In this video - there is already a control point inserted in the trace. That control point allows us to select the two parts of the trace independently. Selecting the trace on the left side of the control point gives us a "Edit Bar" that allows us to convert the trace from "Normal" to "Neckdown."
Neckdown Features
When we use the neckdown region in the board view, we need to be careful of a few things:
- The neckdown region is like a minimum width - not an explicit width.
- We will only see neckdown behavior if there is at least one obstacle that needs to be avoided.
If there are no obstacles and you convert part of a trace to neckdown, you might see this:
In this view we see a trace that doesn't look any different from the normal routing structure trace. This is expected.
If we introduce an obstacle for the neckdown to avoid we will see different behavior:
Notice how the width of the trace approaches the minimum width of the neckdown region near the obstacle. The neckdown region isn't fixed width - it is more adaptive than anything. This may be different than what you expect in other tools.
The clearances for the neckdown region are shown below:
The clearance for the normal routing structure trace and the neckdown region can be different to make it easier to access tight fan-in or fan-out conditions. The blue region is the normal clearance region for the routing structure trace. The yellow region is the clearance for the neckdown region only.
Caution - If the blue region is large, it could conflict with the obstacle. This will result in an unroutable condition.
Outline - DifferentialNeckDown
defstruct DifferentialNeckDown :
pair-spacing: Double|False
trace-width: Double|False
clearance: Double|False
insertion-loss: Double|False
velocity: Double|False
defn DifferentialNeckDown (-- pair-spacing: Double|False = false,
trace-width: Double|False = false,
clearance: Double|False = false,
insertion-loss: Double|False = false,
velocity: Double|False = false) -> DifferentialNeckDown :
All properties of the DifferentialNeckDown
object are optional. If a property is
not provided in the instance, then the pcb-differential-routing-structure
property for the current layer will be used by default. These properties act as overrides of the routing structure properties in the neckdown region of a differential trace.
trace-width
- Width in mm of both of the diff-pair conductors.pair-spacing
- Spacing distance between the conductors of the differential pair in mm.clearance
- Minimum "Net to Net" clearance in mm of the traces. This clearance does not affect thepair-spacing
for the differential pair.velocity
- Signal Propagation Velocity (also known as Group Velocity) of the signals in the neckdown region. This is primarily used for timing constraints. This property is in unitsmm/s
.insertion-loss
- Insertion Loss per unit distance of the signals in the neckdown region. This property is in units ofdB/mm
Usage - DifferentialNeckDown
pcb-differential-routing-structure diff-85 :
layer-constraints(Top):
trace-width = 0.12
pair-spacing = 0.127
clearance = 0.3
velocity = 0.19e12
insertion-loss = 0.008
neck-down = DifferentialNeckDown(
clearance = 0.15
)
In this example - the neckdown region overrides the clearance
but leaves all other properties untouched.
pcb-stackup
The pcb-stackup
statement is used to define the physical layer stack for the PCB. This includes the copper, dielectric, and soldermask layers. The definitions inside the pcb-stackup
statement are used to inform the JITX runtime about the available copper layers and other features.
Signature
pcb-stackup stackup-name (arg1:Type1, ...) :
name = <String|False>
description = <String|False>
<LAYER-1>
...
The expression name stackup-name
uniquely identifies this stackup definition
in the current context.
The argument list (arg1:Type1, ...)
is optional and provides a means of
constructing parameterized stackup definitions.
- Required Parameters
<LAYER-1>
- The stackup is composed of multiple stack() statements which define the construction of the stackup. Each layer can be a different material.
- Optional Parameters
name
- This name is used in the UI as a more user friendly name. If this string is not provided then thestackup-name
expression is used as the stackup's name.description
- This string is defining more meta-data for the stackup - such as the fabricator or manufacturer of this stackup.
Usage
A typical stackup will consist of multiple copper layers separated by resin core and prepreg layers. Here is an example of a 8-layer construction typical of controlled impedance applications:
pcb-material soldermask :
type = Dielectric
dielectric-coefficient = 3.2
loss-tangent = 0.012
pcb-material copper:
type = Conductor
pcb-material prepreg-2x2113:
type = Dielectric
dielectric-coefficient = 4.2
loss-tangent = 0.02
pcb-stackup std-stackup :
name = "8-layer 1.6mm"
val cu-1oz = 0.035
val H = 0.1524
stack(0.019, soldermask)
stack(cu-1oz, copper)
stack(H, prepreg-2x2113)
stack(cu-1oz, copper, "GND1")
stack(H, prepreg-2x2113)
stack(cu-1oz, copper, "SIG1")
stack(H, prepreg-2x2113)
stack(cu-1oz, copper, "GND2")
stack(H, prepreg-2x2113)
stack(cu-1oz, copper, "PWR1")
stack(H, prepreg-2x2113)
stack(cu-1oz, copper, "GND3")
stack(H, prepreg-2x2113)
stack(cu-1oz, copper, "SIG2")
stack(H, prepreg-2x2113)
stack(cu-1oz, copper)
stack(0.019, soldermask)
In this example, all of the layers are separated by a 2x2113 ply prepreg dielectric. The copper layers are all 1oz/ft^2 (35um) thickness.
The strings on the inner copper layers provides overriding names for the copper layers. These names will be used in the layer visualizer to provide an easier mnemonic.
Complex Prepreg Ply Structure
In order to achieve the desired dielectric thickness required for some applications, we need to combine different ply constructions together. We can do this by defining separate materials for each prepreg.
Here is a 4-layer construction where the prepreg layer is a combination of a 1x2113 and 1x2116:
pcb-material core-FR4 :
type = Dielectric
dielectric-coefficient = 4.5
loss-tangent = 0.008
pcb-material soldermask :
type = Dielectric
dielectric-coefficient = 3.2
loss-tangent = 0.012
pcb-material copper:
type = Conductor
pcb-material prepreg-FR4-2113:
type = Dielectric
dielectric-coefficient = 4.5
loss-tangent = 0.008
pcb-material prepreg-FR4-2116:
type = Dielectric
dielectric-coefficient = 4.5
loss-tangent = 0.008
pcb-stackup prepreg-stackup :
name = "4-layer 1.6mm"
stack(0.019, soldermask)
stack(0.035, copper)
stack(0.0762, prepreg-FR4-2113)
stack(0.1067, prepreg-FR4-2116)
stack(0.035, copper)
stack(1.0, core-FR4)
stack(0.035, copper)
stack(0.1067, prepreg-FR4-2116)
stack(0.0762, prepreg-FR4-2113)
stack(0.035, copper)
stack(0.019, soldermask)
Constructs the following stackup:
Notice that the 2113
and 2116
ply prepreg layers are adjacent to each other. This will allow us to construct an accurate PCB stackup table in the drawings.
Checks on Invalid Stackups
One limitation of the pcb-stackup
is that Conductor
type material layers must be separated by a Dielectric
layer.
For example, if we construct a pcb-stackup
with two adjacent copper layers:
pcb-stackup invalid-stackup :
name = "2-layer 1.6mm"
stack(0.019, soldermask)
stack(0.035, copper)
; Invalid Copper Layer Here!
stack(0.035, copper)
stack(1.0, core-FR4)
stack(0.035, copper)
stack(0.019, soldermask)
This will throw the following exception when we use this stackup in the board for the current design:
Uncaught Exception: Stackups cannot have two adjacent conductor layers.
Using Generators
Generators are method of composition. In the pcb-stackup
, we can use a generator to help reduce duplication in our stackup generation.
Note - This is a more complex example. Fear not! These structures will make sense as you gain more experience in the JITX environment.
This example builds on the pcb-material
definitions from the 8-layer example stackup above:
defn add-layer (
H:Double, cu:Double,
--
di-mat:Material = prepreg-FR4-2x2113,
cu-mat:Material = copper
cu-name:String = ?
):
inside pcb-stackup:
stack(H, di-mat)
match(cu-name):
(cu-n:One<String>):
stack(cu, cu-mat, value(cu-n))
(_:None):
stack(cu, cu-mat)
pcb-stackup prepreg-stackup :
name = "8-layer 1.6mm"
val cu-1oz = 0.035
val H = 0.1524
stack(0.019, soldermask)
stack(cu-1oz, copper)
add-layer(H, cu-1oz, cu-name = "GND1")
add-layer(H, cu-1oz, cu-name = "SIG1")
add-layer(H, cu-1oz, cu-name = "GND2")
add-layer(H, cu-1oz, cu-name = "PWR1")
add-layer(H, cu-1oz, cu-name = "GND3")
add-layer(H, cu-1oz, cu-name = "SIG2")
add-layer(H, cu-1oz)
stack(0.019, soldermask)
The inside pcb-stackup
expression converts the function add-layer
into a generator. When this function is called from a pcb-stackup
context, it can generate statements into statements like stack()
into the parent pcb-stackup
context. If you were to call this function from a different context (eg, from a pcb-module
), then this function would throw an error because the stack()
statement would be unknown in that context.
In this case, the add-layer
generator injects a dielectric layer and a copper layer with thicknesses based on the H
and cu
arguments, respectively. It uses default arguments for the dielectric and conductor materials to reduce the number of arguments we must pass.
The cu-name:String = ?
is a special kind of default argument. You can think of cu-name
not as a pure String
but as a Maybe<String>
- see stanza's reference on Maybe
for more info. This drives the need for the match
statement on the cu-name
argument to differentiate between None
and One<String>
.
The constructed pcb-stackup
object from this excerpt should match the 8-Layer stackup defined here.
Statements
Statement | Description |
---|---|
stack | Define a layer in the stackup |
Stackup Layers
The stack
statement inside a pcb-stackup
declares a physical copper or dielectric layer within the stackup.
Signature
stack(<HEIGHT>, <MATERIAL>)
stack(<HEIGHT>, <MATERIAL>, <NAME>)
<HEIGHT>
- This is an instance of typeDouble
that indicates the layer's vertical (Z-axis) height in mm<MATERIAL>
- This is a pcb-material object that defines the physical material for this layer.<NAME>
- This is an instance of typeString
that indicates the name of this layer.- For copper layers, this string will be used in the layer visualizer of the board view.
- If no
<NAME>
string is provided then a default set of names will be used:Top
andBottom
for their respective copper layers- For inner layers, we use
Inner 1
,Inner 2
, etc.
Usage
The pcb-stackup
statement utilizes these stack()
statements to construct the physical board stackup. The ordering of the stack()
statements is important and defines the physical layer order.
pcb-material copper:
type = Conductor
pcb-material soldermask:
type = Dielectric
pcb-material core-45:
type = Dielectric
pcb-stackup simple-2-layer :
stack(0.019, soldermask) ; 0.5mil over conductor
stack(0.035, copper)
stack(1.5, core-45)
stack(0.035, copper)
stack(0.019, soldermask) ; 0.5mil over conductor
pcb-via
The pcb-via
statement is used to define a type of via that can instantiated
in the board view. All via types are defined in code. This allows you to:
- Create a known set of vias with particular characteristics
- Create a known rule set for these vias to avoid deviations.
- Maintain this via set and edit all vias of a particular type in one place.
- Reuse existing via definition sets from project to project.
Outline
pcb-via via-name (arg1:Type1, ...) :
name = <String>
description = <String>
start = <LayerIndex|Side>
stop = <LayerIndex|Side>
diameter = <Double>
hole-diameter = <Double>
type = <MechanicalDrill|LaserDrill>
filled = <True|False>
tented = <Side|Both|True|False>
via-in-pad = <True|False>
backdrill = <BackDrill|False>
The expression name via-name
uniquely identifies this via definition
in the current context.
The argument list (arg1:Type1, ...)
is optional and provides a means of
constructing parameterized via definitions.
- Required Parameters
start
- Sets the starting layer for the via. Setting this to something other than the top layer allows for creating buried or blind vias.stop
- Sets the ending layer for the via.diameter
- Pad diameter in mm for the viahole-diameter
- Drilled or Lasered hole diameter in mm for the via.type
- Identifies the type of via, either mechanical drill or laser microvia.
- Optional Parameters
name
- This name is used in the Board UI as a more user friendly name. If this string is not provided then thevia-name
expression is used as the via's name.description
- This string is defining more meta-data for the via.filled
- Indicates whether this via is filled or not.tented
- Specifies whether or not there is a soldermask opening for this via on top, bottom, or both sides.via-in-pad
- Specifies whether this via is allowed to be placed inside a component's pads.backdrill
- Specifies whether or not this via is backdrilled See BackDrill
Usage
Here are a list of common examples:
Default Through-Hole
Here is a minimal example pcb-via
statement:
pcb-via default-th:
name = "Std TH"
start = Top
stop = Bottom
diameter = 0.6 ; mm
hole-diameter = 0.3 ; mm
type = MechanicalDrill
This constructs what would be considered a typical through-hole via that traverses the board stackup from Top to Bottom layers.
Microvia - Top & Bottom
Here is a minimal example for a microvia on the top side of the board from the top layer to the first inner layer from the top:
public pcb-via top-uvia :
name = "Top-0-1-uvia"
start = LayerIndex(0, Top)
stop = LayerIndex(1, Top)
diameter = 0.15
hole-diameter = 0.1
type = LaserDrill
tented = Top
Another Example but on the bottom side:
public pcb-via bot-uvia :
name = "Bot-0-1-uvia"
start = LayerIndex(1, Bottom)
stop = LayerIndex(0, Bottom)
diameter = 0.15
hole-diameter = 0.1
type = LaserDrill
tented = Bottom
Notice that the LayerIndex
when starting from the bottom of the board has
the layer indices in the reverse order.
pcb-via
with Arguments
Here is an example of constructing a pcb-via
with arguments.
public pcb-via buried (st:Int, end:Int) :
name = to-string("Buried-%_-%_" % [st, end])
start = LayerIndex(st, Top)
stop = LayerIndex(end, Top)
diameter = 0.35
hole-diameter = 0.2
type = MechanicalDrill
Notice that I've used the name
property so that different
invocations of buried
will construct uniquely named vias.
This via can then be used in the pcb-board
definition like this:
public pcb-board mem-board :
stackup = mem-stackup
boundary = board-shape
signal-boundary = board-shape
vias = [default-th-via, top-uvia, bot-uvia, buried(1, 10)]
Notice that we pass the start and end layer for the buried via.
Watch Out!
There are a few things to look out for when defining vias:
- Renaming a via definition in code can cause any instantiated vias in the board design to become invalid.
- Invalid means that the via gets labeled with a red X
- This only applies to vias that have been placed in the board view. If no vias of this type exist in the board yet, then this definition can be renamed at will.
- Examples for changing a via name:
- If there is no
name
property and you change the via's expression name (ie changetop-uvia
tomy-uvia
from the above example) - If there is no
name
property and you add aname
property with a value that is different from the expression name. - If there is an existing
name
property and you changed that string. - If the name is defined via arguments to the definition and the arguments change.
- If there is no
Backdrill
Type
The Backdrill
type is used to define the parameters for a backdrilled via.
val bd = Backdrill(
Bottom, ; Side - Top / Bottom
LayerIndex(4, Bottom), ; Stop Layer
0.4 ; drill diameter, mm
0.6 ; starting pad diameter, mm
0.8 ; Soldermask Opening diameter, mm
1.0 ; Copper Clearance Diameter, mm
)
The Backdrill
can be defined from either the bottom side or the top side.
The second parameter defined the stop layer, which is the copper layer that
is the drill boundary.
In this example - the drill depth is computed from the sum of the copper and dielectric layers from the Bottom layer to 5 copper layer (zero-indexed value) from the bottom. The 5th copper layer from the bottom is the layer that will conduct signal for this via. The 1-4th copper layers from the bottom will be drilled out and not able to conduct or form a stub.
Schematic Symbol Statements
The pcb-symbol
statement defines an object that encodes the schematic representation of a part. This definition includes:
- The electrical connection points for the symbol (ie, the pins).
- The geometric artwork for the symbol.
- Text annotations such as part number, designator, etc.
Each pcb-symbol
defines an internal coordinate frame. All of the geometry, pins, and other features of the symbol are defined with respect to that internal coordinate frame.
The JITX OCDB has a large collection of predefined symbols you can use directly or as templates. The make-box-symbol()
function is a powerful tool to quickly create a consistent looking part library.
For more information about how pcb-symbol
definitions are used, see the symbol statement as it applies to the pcb-component definition.
Signature
pcb-symbol symbol-name (arg1:Type1, ...) :
name = <String|False>
description = <String|False>
preferred-orientation = <SymbolOrientation|False>
backend-substitution(<String>) = <SchematicSymbol>
<PIN-1>
...
<LAYER-1>
...
The expression name symbol-name
uniquely identifies this symbol definition
in the current context.
The argument list (arg1:Type1, ...)
is optional and provides a means of
constructing parameterized symbol definitions.
- Parameters
name
- This name is used in the UI as a more human readable name. If this string is not provided, then thesymbol-name
expression is used as the symbol's name.description
- This string provides documentation and further context when needed.- preferred-orientation - Marks the preferred rotational orientation of this symbol. Default is no preference for any particular orientation.
- backend-substitution - Tool for swapping a schematic symbol depending on which export backend is being used.
<PIN-1>
- A pin statement which defines a connection point to the symbol in the symbol's coordinate frame.<LAYER-1>
- A layer statement which provides a means of inserting symbol artwork into the symbol's coordinate frame.
Non-Box Symbol Example
For more unusual components where box symbols may not apply, you can draw symbols the same way you can draw any collections of shapes in JITX. On imported components, the symbols may appear as collections of lines, shapes, and text.
; Wurth 760390015
pcb-symbol sym-760390015 :
val L = 5.08
val font-size = 1.0
pin p[1] at Point(-5.080, 7.620) with :
direction = Left
length = L
name-size = font-size
pin p[2] at Point(-5.080, 0.0) with :
direction = Left
length = L
name-size = font-size
pin p[3] at Point(-5.080, -7.620) with :
direction = Left
length = L
name-size = font-size
pin p[4] at Point(5.080, -7.620) with :
direction = Right
length = L
name-size = font-size
pin p[5] at Point(5.080, 0.0) with :
direction = Right
length = L
name-size = font-size
pin p[6] at Point(5.080, 7.620) with :
direction = Right
length = L
name-size = font-size
draw("value") = Text(">VALUE", 0.7056, C, loc(0.0, 8.620))
draw("reference") = Text(">REF", 0.7056, C, loc(0.0, 9.620))
draw("foreground") = Circle(-2.794, 6.858, 0.254)
draw("foreground") = Circle(2.540, 6.858, 0.254)
draw("foreground") = Circle(2.540, -0.762, 0.254)
draw("foreground") = Circle(-2.540, -0.762, 0.254)
draw("foreground") = Polyline(0.254, [
Point(-5.080, 7.620)
Point(-2.540, 7.620)])
draw("foreground") = Polyline(0.254, [
Point(-0.6, -7.620)
Point(-0.6, 7.620)])
draw("foreground") = Polyline(0.254, [
Point(0.6, -7.620)
Point(0.6, 7.620)])
draw("foreground") = Polyline(0.254, [
Point(5.080, -7.620)
Point(2.540, -7.620)])
; Coils
draw("foreground") = Polyline(0.254, [
Point(-5.080, 0.0) ; Center Tap
Point(-2.540, 0.0)
Arc(-2.540, 1.270, 1.270, 270.000, 180.0)
Arc(-2.540, 3.810, 1.270, 270.000, 180.0)
Arc(-2.540, 6.350, 1.270, 270.000, 180.0)
Point(-2.540, 7.620)
Point(-5.080, 7.620)
])
draw("foreground") = Polyline(0.254, [
Point(5.080, 7.620)
Point(2.540, 7.620)
Arc(2.540, 6.350, 1.270, 90.000, 180.000)
Arc(2.540, 3.810, 1.270, 90.000, 180.000)
Arc(2.540, 1.270, 1.270, 90.000, 180.000)
Point(2.540, 0.0) ; Center Tap
Point(5.080, 0.0)
])
draw("foreground") = Polyline(0.254, [
Arc(2.540, -1.270, 1.270, 90.000, 180.000)
Arc(2.540, -3.810, 1.270, 90.000, 180.000)
Arc(2.540, -6.350, 1.270, 90.000, 180.000)
Point(2.540, -7.620)
Point(5.080, -7.620)
])
draw("foreground") = Polyline(0.254, [
Point(-5.080, -7.620)
Point(-2.540, -7.620)
Arc(-2.540, -6.350, 1.270, 270.000, 180.000)
Arc(-2.540, -3.810, 1.270, 270.000, 180.000)
Arc(-2.540, -1.270, 1.270, 270.000, 180.000)
])
This generates a symbol that looks like this:
Statements
Here is the list of all of the statements you can use in a pcb-symbol
:
Statement | Description |
---|---|
backend-substitution | Backend Substitution |
description | Description |
layer | Layer |
name | Name |
pin | Pin |
preferred-orientation | Preferred Orientation |
backend-substitution
The backend-substitution
statement is used to customize the symbol generation when exported to a particular backend, like Kicad or Altium.
This
backend-substitution
statement is primarily useful for net symbols like the ground
symbol or the power arrow symbol. Certain backends have a restricted set of net symbols that can be created in these cases. This statement allows the user to customize which
symbol will be used on these backends.
Altium ground and supply symbols are in the ocdb/utils/symbols package of the open-components-database repository.
Signature
backend-substitution(<String>) = <SchematicSymbol>
...
<String>
- A string literal for the backend this statement targets.- "Altium" targets "Altium Designer" export
- "Kicad" targets "Kicad" export.
<SchematicSymbol>
- A schematic symbol definition created using thepcb-symbol
statement.
Usage
For certain symbols, we might want to customize the symbol construction depending on the backend (ie, the export target).
public pcb-symbol ground-sym :
pin p[0] at unit-point(0.0, 0.0)
unit-line([[0.0, 0.0], [0.0, -0.5]])
unit-line(0.1, [[-0.5, -0.5], [0.5, -0.5]])
unit-line(0.1, [[-0.3, -0.7], [0.3, -0.7]])
unit-line(0.1, [[-0.1, -0.9], [0.1, -0.9]])
unit-val([-1.0, -1.5])
preferred-orientation = PreferRotation([0])
backend-substitution("Altium") = altium-power-gnd-power-sym
Here the default symbol geometry gets created on all platforms except Altium, which gets a customized Power Port Symbol, specifically, the "Power Ground" symbol.
The backend gets selected with the set-export-backend function.
Description
The description
statement is the optional descriptive field of a JITX object. Use it to store a description of the object for human designers reading JITX, and to make the object easier to find via text search. This description also shows up in the UI, such as the design explorer, to provide more insight into particular components and modules in the design.
Signature
description = <String|False>
This statement is optional. The default value will be false
in case no description
statement is found in a definition.
Any string is a valid description
value.
Each JITX definition may have exactly one description
statement. If more than one statement is encountered in a definition, then a DuplicateCStmtError
exception will be raised.
Examples
; We can use string literals to describe a particular component.
pcb-component analog-devices-ADM7150 :
description = "800 mA Ultralow Noise, High PSRR, RF Linear Regulator"
; We can use string formatting to construct descriptions based on variables
; or arguments to a JITX Definition
pcb-module band-pass-filter (high-cut:Double, low-cut:Double) :
description = to-string(
"Band-pass Filter - Highpass = %_ Hz and Lowpass = %_ Hz." % [high-cut, low-cut]
)
Symbol Layers
The draw
statement inside a pcb-symbol
adds symbol artwork on a named schematic layer.
Signature
draw(<String>) = <Shape>
- The
<String>
is an identifier that specifies which layer in the schematic symbol the provided artwork will be drawn to. The most commonly used layers are"foreground"
and"background"
. - The
<Shape>
is a geometric shape likeCircle
,Rectangle
, etc that will be drawn as artwork on the associated layer.
Usage
pcb-symbol test:
...
draw("foreground") = Rectangle(10.0, 10.0)
draw("background") = loc(0.0, 20.0) * Circle(5.0)
draw("background") = loc(0.0, -20.0, 45.0) * Rectangle(10.0, 10.0)
Notice that we can use the loc
function to translate and rotate shapes within the coordinate frame of the pcb-symbol
Example Rendering:
Description
draw
places geometry on a named layer in the schematic. The most commonly used layers in JITX schematics are "foreground"
and "background"
.
Name
The name
statement is the optional name field of a JITX object. Use it to store a descriptive name as a String
. This name will often be used in the UI in place of the object's expression name for better readability.
Signature
name = <String>
This statement is optional. The default value will be the definition's symbol name in case no name
statement is found in a definition.
Any string is a valid name
value.
Usage
Literal String Names
pcb-component component :
name = "ADM7150"
pcb-module band-pass-filter :
name = "Band-pass filter"
The examples name = "ADM7150"
and name = "Band-pass filter"
use a String
literal for the name.
Formatted Strings
pcb-pad smd-pad (anchor:Anchor, w:Double, h:Double) :
name = to-string("%_x%_ %_ SMD Pad" % [w,h,anchor])
pcb-landpattern test-lp:
pad p[1] : smd-pad(C, 0.6, 0.7) at loc(x0,y0)
We can also construct strings using formatting routes and parameter arguments for a particular definition. In this example, the constructed smd-pad
name property would be 0.6x0.7 C SMD Pad
pin
for Symbols
The pin
statement in the pcb-symbol
context is used to define the connection points of a schematic symbol.
Signature
; Point Form
pin <Ref> at <Point>
; Annotated Line Form
pin <Ref> at <Point> with:
direction = <Dir>
length = <Double>
number-size = <Double|False>
name-size = <Double|False>
<Ref>
- Name of the pin that will be constructed.- The
Ref
can be a standard identifier likeVIN
or it can be an indexed reference likeD[1]
,D[2]
, etc.
- The
<Point>
- An instance of typePoint
.
Usage
The pin
statement is used to define the connection points on a schematic symbol. There are two forms for the pin connections:
- Point Form - In this form, the connection point does not have a line associated with it.
- Further, the name and pad number are hidden by default.
- This can be useful for constructing symbols where a line might get in the way or prevent the formation of geometry in the preferred way.
- Annotated Line Form - In this form, the connection point has a line associated with it by default.
Annotated Line Form
Here is an example pin
statement in annotated line form:
pin VIN at Point(0.0, 0.0) with:
direction = Left
length = 3.0
name-size = 0.85
number-size = 0.85
Here is rendering of the anatomy of a symbol pin:
In this view:
- The text
p[0]
is thepad ref
- this is what pad/hole on the component landpattern that this pin maps to. - The text
VIN
is thepin ref
- this is the name of this pin in the circuit and as shown on the schematic. - This pin has a direction that points to the left.
- Notice that the symbol body is on the right and that the pin's direction is direction it points out from the symbol body.
- The red
X
at the tip of the pin is the connection point where schematic wires will be drawn to/from.
Optional Pad and Pin Ref Text
In the annotated line form, you can optionally drop the name-size
and number-size
parameters from the pin
statement. This will cause the pin ref
and/or pad ref
to be hidden by default:
pin VIN at Point(0.0, 0.0) with:
direction = Left
length = 3.0
number-size = 0.85
In this case, the pin would be shown as:
This is useful for constructing more idiomatic schematic symbols where the pin name is not typically shown - for example, in an op amp symbol:
preferred-orientation
The preferred-orientation
statement is used to as a hint to the schematic layout engine. It provides a set of preferred orientations of this symbol. The layout optimization engine will then try its best to keep the symbols oriented in those directions.
Signature
preferred-orientation = <SymbolOrientation>
The <SymbolOrientation>
is an instance of type SymbolOrientation
. There are two derived types for SymbolOrientation
in the JITX runtime:
AnyRotation()
- This tells the schematic layout engine that there is no preferred orientation for this component.PreferRotation(rotations:Tuple<Int>)
- This allows you to specify one of four standard orientations for the schematic symbol.- Rotation Mapping:
0
=>0°
1
=>90°
2
=>180°
3
=>270°
- Rotation Relationship:
90° * i
- Each rotation is counter-clockwise around the Z-axis
- The values
0 through 3
are the only accepted values.
Usage
There are many uses for this functionality, but the motivating use case is for net symbols like ground and power symbols:
public pcb-symbol altium-ground-sym :
name = "POWER-GROUND"
pin p[0] at unit-point(0.0, 0.0)
unit-line([[0.0, 0.0], [2.54, 0.0]])
unit-line(0.1, [[2.54, -1.4], [2.54, 1.4]])
unit-line(0.1, [[3.81 -0.93], [3.81, 0.93]])
unit-line(0.1, [[5.08, -0.46], [5.08, 0.46]])
unit-val([7.5, -2.5])
preferred-orientation = PreferRotation([3])
The PreferRotation([3])
results in the following preferred orientation:
Note that this rotation is about the origin in the pcb-symbol
frame of reference.
Struct Statements
pcb-struct
is a way to store a collection of related variables.
Example
Right now we need to use a fully qualified name to define a new pcb-struct
. So if we wanted an struct named:
GenericPin
And we're defining it in a package named
defpackage ocdb/utils/property-structs
Then the struct needs to be defined as follows:
public pcb-struct ocdb/utils/property-structs/GenericPin :
max-voltage:Toleranced|RelativeVoltage ; Maximum voltage that can be applied to pin (V)
rated-esd:Double
All together this is what it looks like to define and use a
pcb-struct
:
defpackage ocdb/utils/property-structs:
...
public pcb-struct ocdb/utils/property-structs/GenericPin :
max-voltage:Toleranced|RelativeVoltage ; Maximum voltage that can be applied to pin (V)
rated-esd:Double
val generic-props = GenericPin(min-max(-0.3 5.0), 1500.0)
println(max-voltage(generic-props))
When we create a value that uses a struct, the variables are given in the order defined in the pcb-struct
. In the above example:
min-max(-0.3 5.0)
gets assigned tomax-voltage
1500.0
gets assigned torated-esd
We can then access the information by the name defined in the pcb-struct
. max-voltage(generic-props)
fetches the value stored in the field max-voltage
of a GenericPin
.
JITX Types Reference
This is the reference page for JITX types.
Top Level Definitions
Top-level definitions are subtypes of JITXDef
.
public deftype JITXDef <: Hashable & Equalable
;pcb-landpattern
public defstruct LandPattern <: JITXDef:
...
;pcb-pad
public defstruct Pad <: JITXDef:
...
;pcb-symbol
public defstruct SchematicSymbol <: JITXDef:
...
;pcb-rules
public defstruct Rules <: JITXDef:
...
;pcb-material
public defstruct Material <: JITXDef:
...
;pcb-board
public defstruct Board <: JITXDef:
...
;pcb-stackup
public defstruct Stackup <: JITXDef:
...
While most top-level definitions follow this format, components, modules, and bundles have additional types. Instantiables
include modules and components, and since they support individual and array representations, they are subtypes of JITXDef
and InstantiableType
. Bundles
are one of three forms that can be declared as a port in a pcb-module or pcb-component. They are subtypes of JITXDef
and PortType
.
public deftype InstantiableType
;pcb-module and pcb-component
public defstruct Instantiable <: JITXDef & InstantiableType:
...
public defstruct InstantiableArray <: InstantiableType:
...
public deftype PortType
public defstruct SinglePin <: PortType:
...
;pcb-bundle
public defstruct Bundle <: JITXDef & PortType:
...
public defstruct PortArray <: PortType:
...
Local Definitions
Local definitions are subtypes of JITXObject
.
public deftype JITXObject <: Hashable & Equalable
;Pins of modules, components, or bundles
public defstruct Pin <: JITXObject:
...
;Land pattern pads
public defstruct LandPatternPad <: JITXObject:
...
;Symbol pins
public defstruct SymbolPin <: JITXObject:
...
;Instances of modules or components
public defstruct Instance <: JITXObject:
...
;Nets of modules
public defstruct Net <: JITXObject:
...
Self
self
is an Instance
than can be referenced within a pcb-module
or pcb-component
to access or modify fields of that definition.
JITX Patterns Cheat Sheet
The JITX language is embedded into the LB Stanza Programming Language. A comprehensive guide to learning stanza can be found in the reference, Stanza by Example. A short guide on stanza expressions and syntax can also be read here.
Common Code Patterns
for
loops
A for loop takes the form
for <thing> in <sequence> <op> :
<body>
Where thing
is an identifier, sequence
is an expression of type Seqable
, and op
is an "operating" function that applies body
to thing
. Here are some common examples :
val values = [1, 2, 3]
for value in values do :
println(value)
; prints :
; 1
; 2
; 3
do
is the most common operating expression and just applies the body of the loop to the value in the sequence.
Another common operator is filter
, which is used to loop over the sequence
and remove items.
val values = [1, 2, 3]
val odds =
for value in values filter :
value % 2 != 0
for odd in odds do :
println(odd)
; prints :
; 1
; 3
More examples of for loops can be found in Stanza by Example, and a list of operating functions is available in the reference.
match
expressions
The match
expression is a conditional expression that evaluates one of multiple branches depending on the types of its arguments. The most common form is a single argument match.
defn my-function (arg:Int|Double|True|False) :
match(arg) :
(i:Int) :
println("arg is an Int:%_" % [i])
(d:Double) :
println("arg is a Double:%_" % [d])
(b:True|False) :
println("arg is a boolean:%_" % [b])
match
can take multiple arguments.
defn my-function (x:Int|Double, y:Int|Double) :
match(x, y) :
(x:Int, y:Int) :
println("x and y are Ints.")
(x:Double, y:Double) :
println("x and y are Doubles.)
(x, y) :
println("x and y are of different types!)
A common pattern is something like this :
defn my-function (arg:Int|False) :
match(arg) :
(arg:Int) :
do-something(arg)
(f:False) :
; do nothing
false
A shorthand for this syntax is
defn my-function (arg:Int|False) :
match(arg:Int) :
do-something(arg)
switch
expressions
The switch
expression is another conditional expression that evaluates multiple branches, it's syntactic sugar for chains of if/else if expressions.
defn arrive-or-leave (arg:String) :
switch(arg) :
"hello" :
hello()
"goodbye" :
goodbye()
else :
fatal("The caller has bad manners.")
supports
statements
Supports statements declare what bundle(s) a component or module supports and how the bundle pins are mapped to the ports of the component or module.
; Bundles may be used inside pcb-components or
; pcb modules
; The mapping goes
; bundle.pin-ref => self.pin-ref
supports bundle :
bundle.p[1] => self.p[2]
; ... rest of mappings to the bundle ...
requires
statements
Requires statements are used in pcb-module
definitions to define what support
s they need.
; inside pcb-module
require <ref> : <bundle> from <instance>
The pin solver will automatically map pins that supports
the bundle
that the module requires to an alias, ref
.
In addition to using require
inside of a pcb-module
, require
statements can be "chained" inside of supports
statements.
pcb-component mcu :
port p: pin[128]
for n in 10 to 30 do :
supports gpio :
gpio.p => self.p[n]
supports spi-controller():
require io : gpio[4]
spi-controller().copi => io[0].p
spi-controller().cipo => io[1].p
spi-controller().cs => io[2].p
spi-controller().sck => io[3].p
pin-properties
tables
pin-properties
tables are a mechanism for declaring metadata about the pins in a pcb-component
. It is used for commands like assign-landpattern
, assign-symbol
, and make-box-symbol
.
; Declare pin properties: the names of pins and pads they are mapped
; to on the associated landpattern
pcb-component component :
;Each row has format:
pin-properties :
[pin:Ref | pads:Int ... ]
[a | 1 ]
[b | 2 ]
[c | 3 ]
[gnd | 4 5 6 7 8 ]
[d+ | 9 ]
[d- | 10 ]
make-box-symbol()
assign-landpattern(mylandpattern)
pcb-component
definitions
See the reference for more.
pcb-component component :
name = "My awesome component"
mpn = "MPN"
manufacturer = "Manufacturer"
description = "This is an example component."
; ... component statements ...
pcb-module
definitions
See the reference for more.
pcb-module module :
; Define some metadata of the module
name = "My Module"
description = "This is an example module"
; Declare some ports
port io: bundle
; Declare some instances
inst U: component
; Net ports and instances together
net (io.p[1], U.p[1])
; Perhaps add some layer geometry
layer(Silkscreen("F-Silk", Top)) = Text("REV 1.0", 2.54, C, loc(0.0, 0.0))
; Add instances to schematic groups
schematic-group(U) = grouped-insts
; Add the instances to schematic groups
pcb-landpattern
definitions
See the reference for more.
pcb-landpattern my-landpattern :
; define a pad
pad p[1] : smd-pad(Circle(1.0)) at loc(0.0, 0.0) on Top
; Add custom soldermask layers
layer(SolderMask(Top)) = Rectangle(1.25, 1.25)
pcb-component
to pcb-symbol
mappings
Symbol pins are mapped to component pins using the following syntax :
pcb-component my-resistor :
port p: pin[[1, 2]]
; sym is an expression corresponding to a generated
; pcb-symbol
val sym = resistor-sym()
; The mapping is:
; <component-pin> => <symbol>.<symbol-pin>
symbol =
sym(p[1] => sym.p[1],
p[2] => sym.p[2])
pcb-component
to pcb-landpattern
mappings
Landpattern pins are mapped to component pins using the following syntax :
pcb-component my-resistor :
port p: pin[[1, 2]]
; lp is an expression corresponding to a generated
; pcb-landpattern
val lp = ipc-two-pin-landpattern("0201")
; The mapping is:
; <component-pin> => <landpattern>.<landpattern-pad>
landpattern =
lp(p[1] => lp.p[1],
p[2] => lp.p[2])
Getting and setting properties
; set a property
property(self.property-name) = expression
; get a property
val property = property(self.property-name)
; check if a property is set
has-property?(self.property-name)
; optionally check if a property is set and
; perform some logic with the value
match(property?(self.property-name)) :
(one:One) :
val property = value(one)
(none:None) :
; property is not set
Adding passive components
ocdb/utils/generic-components
has a few generators for querying jellybean components from
the JITX parts database. See the design variables reference for configuration with
global design parameters.
defpackage my-design :
import jitx
import jitx/commands
import ocdb/utils/design-vars
import ocdb/utils/generic-components
; define the minimum package size for the design
MIN-PKG = "0601"
pcb-module module :
; Drop some passives in a module
; Query a generic 1k ohm chip resistor
inst R1 : chip-resistor(1.0e3)
; Query a generic 1k ohm chip resistor with 5% tolerance
inst R2 : chip-resistor(1.0e3, 0.05)
; Query a generic 4.7nF ceramic cap
inst C1 : ceramic-cap(4.7e-9)
; Query a generic 4.7nF ceramic cap with 5% tolerance
inst C1 : ceramic-cap(4.7e-9)
Named net
s
Nets can be given a name, but it must have one character of whitespace between the name and arguments!
pcb-module module :
net name (a, b, c)
; net name(a, b,c) <--- invalid!
Learning Stanza: quick reference
JITX is implemented in the LB Stanza (Stanza) programming language. We like stanza because it supports types to help write correct code, and also allows higher-level programming. We can write Stanza programs inline with JITX code, to generate designs, components, and modules programmatically.
We do not need all of Stanza to develop with JITX; this document provides a short walkthrough of commonly-used Stanza features.
Comments
; this is a comment
;<A>
This is a block comment
;<B>
block comments can be nested with optional tags.
;<B>
;<A>
Basics
defpackage learn-stanza-in-y:
import core
import collections
;=========================================================================
; The basics, things you'd find in most programming languages
;=========================================================================
; Variables can be mutable (var) or immutable (val)
var mutable = "this one can be"
val immutable = "this string can't be changed"
; The basic data types (annotations are optional)
val an-int: Int = 12345
val a-long: Long = 12345L
val a-float: Float = 1.2345f
val a-double: Double = 3.14159
val a-string: String = "this is a string"
val a-multiline-string = \<tag>
this is a multi-line string literal.
<tag>
; print formatted strings with println() and "..." % [...]
println("this is a formatted string %_ %_" % [mutable, immutable])
; Stanza is optionally typed, and has a ? (any) type.
var anything:? = 0
anything = 3.14159
anything = "a string"
; Stanza has basic collections such as Tuples, Arrays, Vectors, HashTables, etc.
val tuple: Tuple<?> = [mutable, immutable]
val array = Array<?>(3)
array[0] = "string"
array[1] = 1
array[2] = 1.23455
; array[3] = "out-of-bounds" ; arrays are bounds-checked
val vector = Vector<?>()
vector[0] = "string"
vector[1] = 1
vector[2] = 3.14159
val hash-table = HashTable<String, ?>()
hash-table["0"] = 0
hash-table["1"] = 1
hash-table["2"] = 1
Functions
; Functions are declared with the `defn` keyword
defn my-function (arg:?) : ; note the space between identifier and arg list
println("called my-function with %_" % [arg])
my-function("arg") ; note the lack of a space to call the function
; If you try to define a function without a space, the syntax highlighter
; should give you a gentle nudge to fix it (ie, the red text and underline)
defn invalid-func(arg1, arg2) -> Int:
...
; functions can be declared inside another function and capture their environment. This is called a closure.
defn outer (arg):
defn inner ():
println("outer had arg: %_" % [arg])
inner()
outer("something")
; functions are "first-class" in stanza, meaning you can assign variables
; to functions and pass functions as arguments to other functions.
val a-function = outer
defn do-n-times (arg, func, n:Int):
for i in 0 to n do :
func(arg)
do-n-times("argument", a-function, 3)
; sometimes you want to define a function inline, or use an anonymous function.
; for this you can use the syntax:
; fn (args):
; ...
do-n-times("hello", fn (arg): println(arg), 2)
; there is a shorthand for writing anonymous functions, the above can be written
do-n-times("hello", { println(_) }, 2)
; the short hand works for multiple arguments as well.
val multi-lambda = { println(_ + 2 * _) }
multi-lambda(1, 2)
; You can reference the arguments explicitly as well
; with `_0`, `_1`, for the `Nth` argument.
val lambda-args = { println(_0, + (3 * _1)) }
User Defined Types
; Structs are declared with the `defstruct` keyword
defstruct MyStruct:
field:String
; constructors are derived automatically
val my-struct = MyStruct("field:value")
; fields are accessed using function-call syntax
println(field(my-struct))
; Stanza supports subtyping with a "multimethod" system based on method
; overloading.
;
; This effectively defines an interface for type `Mytype` consisting
; of a single method `a-method`
deftype MyType
defmulti a-method (m:MyType)
defstruct Foo <: MyType:
fab:Int
defmethod a-method (a-foo: Foo):
println("called a-method on a Foo")
defstruct Bar <: MyType:
bab:Double
defmethod a-method (a-foo: Bar):
println("called a-method on a Bar")
The Type System
; True and False are types with a single value.
val a-true: True = true
val a-false: False = false
; You can declare a union type, or a value that is one of a set of types
val a-boolean: True|False = true
val another-boolean: True|False = false
; You can pattern match on types
match(a-boolean):
(t:True): println("is true")
(f:False): println("is false")
; You can match against a single possible type
match(a-boolean:True):
println("is still true")
else:
println("is not true")
; You can compose program logic around the type of a variable
if anything is Float :
println("anything is a float")
else if anything is-not String :
println("anything is not an int")
else :
println("I don't know what anything is")
Control Flow
; stanza has the standard basic control flow
val condition = [false, false]
if condition[0] :
; do something
false
else if condition[1] :
; do another thing
false
else :
; whatever else
false
; there is also a switch statement, which can be used to pattern match
; on values (as opposed to types)
switch(anything):
"this": false
"that": false
"the-other-thing": false
else: false
; for and while loops are supported
while condition[0]:
println("do stuff")
; More on For Loops shortly.
for i in 0 to 10 do:
vector[i] = i
; stanza also supports named labels which can function as break or return
; statements
defn another-fn ():
label<False> return:
label<False> break:
while true:
if condition[0] is False:
break(false)
return(false)
; Stanza has exceptions and provides a syntax similar to Python:
try:
some-func()
throw(Exception("Abort!"))
catch (e:Exception):
println("Error: %~" % [e])
finally:
println("We made it!")
; For a comprehensive guide on Stanza's advanced control flow, check out
; this page: http://lbstanza.org/chapter9.html from Stanza-by-Example
For Loop Operators
; The for-loop has a special expression at the end called the "Operating Function"
; In most cases - it is simply the `do` operator:
for i in 0 to 10 do:
println("Index: %_" % [i])
; However the operating function can _do_ so much more!
; Operating functions typically have the form:
defn some-op (func, xs:Seqable) -> False:
...
; Here the first argument is a function, and the second argument
; is a sequence of objects
; Referring to our previous example:
for i in 0 to 10 do:
println("Index: %_" % [i])
; When used in a for-loop, the body of the for-loop becomes the function
; `func`.
defn body (i) -> False :
println("Index: %_" % [i])
; The value `i` becomes the argument to `func`. The expression `0 to 10` becomes `xs`.
; There are many operating functions - `seq`, `map`, `filter`, etc. - defined in the stanza core library.
; https://lbstanza.org/reference.html#anchor478
Sequences
; for "loops" are sugar for a more powerful syntax.
val xs = [1, 2, 3]
val ys = ['a', 'b', 'c']
val zs = ["foo", "bar", "baz"]
for (x in xs, y in ys, z in zs) do :
println("x:%_, y:%_, z:%_" % [x, y, z])
;xs, ys, and zs are all "Seqable" meaning they are Seq types (sequences).
; Sequences are called "iterators" in other languages, like Python, and
; provide similar abstractions.
;
; A common sequence task is concatenating sequences. This is accomplished
; using the `seq-cat` operating function. This is analogous to
; "flattening" iterators
val concat = to-tuple $
for sequence in [xs, ys, zs] seq-cat:
sequence
; we can also use a variation to interleave the elements of multiple sequences
val interleaved = to-tuple $
for (x in xs, y in ys, z in zs) seq-cat :
[x, y, z]
println("[%,] [%,]" % [concat, interleaved])
; Another common task is mapping a sequence to another, for example multiplying
; all the elements of a list of numbers by a constant. To do this we use `seq`.
var numbers = [1.0, 2.0, 3.0, 4.0]
numbers = to-tuple $
for n in numbers seq :
2.0 * n
println("%," % [numbers])
if find({_ == 2.0}, numbers) is-not False :
println("found it!")
; or maybe we just want to know if there's something in a sequence
var is-there = for n in numbers any? :
n == 2.0
; since this is "syntactic sugar" we can write it explicitly using an
; anonymous function
is-there = any?({_ == 2.0}, numbers)
Documentation
;=========================================================================
; Documentation
;=========================================================================
;
; Top level statements can be prefixed with the "doc" field which takes
; a string value and is used to autogenerate documentation for the package.
doc: \<doc>
# Document Strings
```stanza
val you-can = "include code snippets, too"
```
To render documentation as markdown (compatible with mdbook)
```bash
stanza doc source.stanza -o docs
```
<doc>
defn docfn () : false
API Reference for the jitx/commands
package
Part Query API
To use these functions, import the package jitx/commands
and the package jitx
where the enums are defined.
import jitx
import jitx/commmands
defn set-main-module (module:Instantiable)
Define the "main" module of the current design.
Syntax
pcb-module main-module :
...
set-main-module(main-module)
Description
The main module is the root of the entire JITX design, and is analogous to a "root" schematic sheet.
defn set-board (board:Board)
Sets the pcb-board
object used by the current design.
Syntax
pcb-board my-board :
...
set-board(my-board)
Description
This specifies the pcb-board
which defines the stackup,
board outline, and signal boundary of the physical board
used in the design.
Multi-board designs require separate calls to set-current-design
,
set-board
, etc.
defn set-rules (rules:Rules)
Set the design rules for the current design.
Syntax
pcb-rules jlc-pcb-rules :
...
set-rules(jlc-pcb-rules)
Description
The design rules takes the name of a pcb-rules
as an
argument and configures the design rules used for generators
and checks in the current design. This will be cleared on
calls to set-current-design
.
defn set-default-rules (rules:Rules)
Sets the default rules for all designs in the current program.
Syntax
pcb-rules jlc-pcb-rules :
...
set-default-rules(jlc-pcb-rules)
Description
Default rules differ from design rules set in set-rules
in that
they are not cleared between designs, and serves as temporary or
default values for design rules so generators can make progress
even if you have not set the rules for your design.
Default rules can also be used to share design rules in multi-design programs.
defn clear-default-rules ()
Syntax
clear-default-rules()
Description
Removes default rules if they are set. This is primarily used for testing.
To use these functions, import the package jitx/commands
and the package jitx
where the enums are defined.
import jitx
import jitx/commmands
defn get-merged-physical-design-module () -> Instantiable
Obtain the physical-design-transformed "main" module of the current design.
Syntax
val new-module = get-merged-physical-design-module()
Description
Returns a new module which incorporates all of the physical design information incorporated into the "main" module of the current design.
defn print-def (def:JITXDef)
Print the ESIR definition as ASCII. Not very human readable. Use Design Explorer to see this data structured for human consumption.
Syntax
pcb-module my-module :
...
print-def(my-module)
print-def
will print out the fully expanded definition. JITX
definitions include pcb-board
, pcb-module
, pcb-rules
,
pcb-stackup
, pcb-component
, pcb-pad
, pcb-landpattern
,
etc.
defn get-def-string (def:JITXDef) -> String
Get the string representation of the ESIR definition as ASCII. Returns the same string that print-def prints.
Syntax
pcb-module my-module :
...
val my-module-str = get-def-string(my-module)
get-def-string
will return the fully expanded definition string.
JITX definitions include pcb-board
, pcb-module
, pcb-rules
,
pcb-stackup
, pcb-component
, pcb-pad
, pcb-landpattern
, etc.
defn transform-component (body:() -> ?, module:Instantiable) -> Instantiable
Applies body
on component
to return a newly transformed Instantiable.
Example
pcb-component my-component :
; ...
val my-modified-component = within transform-component(my-component) :
inside pcb-component :
override landpattern = my-new-landpattern
override symbol = my-new-symbol
property(self.transformed) = true
This example returns a modified component which has the same name,
descriptions, pins, and so on, of the original component, but now has a
different landpattern and symbol, and a new property. Note the use of the
override
keyword to allow setting the landpattern and symbol after they have
already been set.
Because override
is not yet implemented for many component attributes which
can only be set once, such as name, description, mpn, manufacturer, etc.,
these will only be able to be set if the original component definition did not
set them. override
is also not yet implemented for symbols
(setting
multiple symbol units on one component).
transform-component
is mainly intended to be used with modify-components
.
defn assign-landpattern (package:LandPattern) -> Null
Map a landpattern to a component using pin properties.
Syntax
public pcb-component component :
manufacturer = "Texas Instruments"
mpn = "FDC2214QRGH"
pin-properties :
[pin:Ref | pads:Int ... | side:Dir ]
[i2c.scl | 1 | Left ]
[i2c.sda | 2 | Left ]
[CLKIN | 3 | Left ]
[ADDR | 4 | Left ]
[INTB | 5 | Left ]
[SD | 6 | Left ]
[VDD | 7 | Left ]
[GND | 8 | Left ]
[IN0A | 9 | Right ]
[IN0B | 10 | Right ]
[IN1A | 11 | Right ]
[IN1B | 12 | Right ]
[IN2A | 13 | Right ]
[IN2B | 14 | Right ]
[IN3A | 15 | Right ]
[IN3B | 16 | Right ]
val lp = qfn-landpattern(0.5, 3.8, 16, 0.25, 0.4, [2.6 2.6])
assign-landpattern(lp)
Description
The assign-landpattern
convenience function is a helper for mapping a landpattern to a
pcb-component
using the pin-properties
table. The table must contain a pads
column
whose type is | pads: Int ...|
, | pads: Ref ...|
or |pads: Int|Ref ...|
. Multiple
entries in the pads
column can be used to assign a component pin to multiple pads.
defn assign-symbol (symbol:SchematicSymbol) -> Null
Assign a symbol to a pcb-component
Syntax
public pcb-component component :
name = "142-0761-881"
manufacturer = "Johnson / Cinch Connectivity Solutions"
mpn = "142-0761-881"
reference-prefix = "J"
pin-properties :
[pin:Ref |pads:Int ... | side:Dir]
[sig | 1 | Left ]
[gnd | 2 3 | Down ]
assign-symbol(coax-sym)
assign-landpattern(johnson-142-0761-881-pkg)
Description
The assign-symbol
function uses the pin
column in a pin-properties
table
to map the pins of a component to the pins of a schematic symbol. The symbol
pin names must match the component pin names
defn clear-dbquery-cache () -> Null
Clear the cache on disk used for part queries with dbquery so that the parts gets refreshed next time the queries are run.
defn transform-module (body:() -> ?, module:Instantiable) -> Instantiable
Applies body
on module
to return a newly transformed Instantiable. This is how to run custom passes on your design module.
Syntax
pcb-module main-module :
; ...
defn generate-power () :
inside pcb-module :
; ...
val module-with-power = transform-module(generate-power, main-module)
Description
The transform-module
function takes two arguments, a function body
and pcb-module
module
.
The compiler will apply body
to the module
defn modify-components (modify-component:(Instantiable) -> Instantiable|False, module:Instantiable) -> Instantiable
Transforms module
by modifying every component definition by calling the
user-supplied function modify-component
on them. Whenever
modify-component
returns a new Instantiable
, the component definition will
be swapped in place. However, this substitution only takes effect in the new
module returned by modify-components
. If modify-component
instead returns
false
, the component will be left as is.
To make useful changes, modify-component
can use the transform-component
function to add statements to the given component definition.
Example
defn standardize-parts (module:Instantiable) -> Instantiable :
within component = modify-components(module) :
if name(component) == "my-resistor" :
println("standardizing component %_" % [name(component)])
within transform-component(component) :
inside pcb-component :
val [p1,p2] = pins(self) as [Pin Pin]
val symb = my-preferred-resistor-symbol
val [q1,q2] = pins(symb) as [Pin Pin]
override symbol = symb(p1 => q1, p2 => q2)
else :
false ; don't change the component
val new-design = standardize-parts(original-design)
set-main-module(new-design)
This example overrides the symbol of every component named my-resistor
to
my-preferred-resistor-symbol
, mapping the pins in the order they were given.
defn run-evals (module:Instantiable) -> Instantiable
Runs all eval-when
blocks nested within module
to return a newly evaluated Instantiable.
Syntax
pcb-module main-module :
eval-when has-property?(self.voltage) :
println("Voltage assigned!")
defn assign-voltage () :
inside pcb-module :
property(self.voltage) = 3.3
val transformed-module = transform-module(assign-voltage, main-module)
val evaluated-module = run-evals(transformed-module)
Description
eval-when
statements need to be driven by the design generator code. The run-evals
command is
used to control this.
defn run-checks (filename:String) -> CheckSummary
Runs all checks statements, saves results in a file named filename
, and returns a CheckSummary
.
Syntax
pcb-check check-5V (a:JITXObject) :
#CHECK(has-property?(a.voltage))
#CHECK(property(a.voltage) is Double)
#CHECK(property(a.voltage) == 5.0)
pcb-module mymodule :
pin x
property(x.voltage) = 5.0
check check-5V(self)
set-main-module(mymodule)
run-checks("5V-check.txt")
Description
The run-checks
command is used to run pcb-check
s on a design. Checks are arbitrary
DRC, ERC, or other static analsyses defined in code to validate a design programmatically.
defn apply-variants (vs:Tuple<String>, module:Instantiable) -> Instantiable
Apply Variants to a design to obtain a new design
Or no Variant is used if the argument is empty
Syntax
pcb-module my-module :
inst a : component-a
variant "DNP" :
do-not-populate(a)
val new-module = apply-variants(["DNP"] my-module)
set-main-module(new-module)
Description
The apply-variants
command is used to transform a module into another module with the Variants applied.
defn freeze-reference-designators () -> Instantiable
Freeze all the reference designators, so they will not be recomputed.
Syntax
val lowered-module = run-final-passes(module)
set-main-module(lowered-module)
set-main-module(freeze-reference-designators())
Description
The freeze-reference-designators
command stops the JITX compiler from
generating new reference designators for existing components in a design.
defn assign-pins () -> Instantiable
Explicitly run pin assignment before automatic placement, visualization, or export.
Syntax
pcb-module my-module :
pin x
supports gpio :
gpio.gpio => x
require mygpio: gpio from self
set-main-module(my-module)
val my-module-assigned = assign-pins()
Description
JITX will automatically run pin assignment prior to board placement, export, and visualization.
Pin assignment can be run manually using the assign-pins
function, which can be useful
when running checks or debugging.
Running assign-pins?()
instead of assign-pins
will return False
if there is no valid pin assignment.
defn view-board (view-mode:ViewMode) -> Null
defn view-board () -> Null
Visualize the generated board.
Syntax
; Default: update the view
view-board()
; Visualize, panning to the center of the view and zooming to fit
view-board(NewView)
; Visualize, but maintain the same pan/zoom state.
view-board(UpdateView)
Description
The view-board
command is used to visualize the generated circuit board.
defn view-schematic (view-mode:ViewMode) -> Null
defn view-schematic () -> Null
Visualize the generated schematic.
Syntax
; Default: update the view
view-schematic()
; Visualize, panning to the center of the view and zooming to fit
view-schematic(NewView)
; Visualize, but maintain the same pan/zoom state.
view-schematic(UpdateView)
Description
The view-schematic
command is used to visualize the generated circuit board.
defn view-sub-schematic (objects:Tuple<JITXObject>, schematic-group?:True|False) -> Null
defn view-sub-schematic (object:JITXObject, schematic-group?:True|False) -> Null
defn view-sub-schematic (object:JITXObject) -> Null
defn view-sub-schematic (objects:Tuple<JITXObject>) -> Null
Visualize the generated sub-schematic for a list of nets and instances.
Nets can be single nets, net arrays or bundle nets.
Instances can be component or module instances. They can be single instances or instance arrays.
Syntax
; Objects given to view-sub-schematic need to reference the design main module.
set-main-module(main-module)
; Visualize a list of nets and instances. Default: no schematic groups.
view-sub-schematic([main-module.gnd, main-module.P3V3])
; Visualize a list of nets and instances with schematic groups.
view-sub-schematic([main-module.gnd, main-module.P3V3], true)
; Visualize a single net or instance. Default: no schematic groups.
view-sub-schematic(main-module.sensors)
; Visualize a single net or instance with schematic groups.
view-sub-schematic(main-module.sensors, true)
Description
The view-sub-schematic
command is used to visualize a subset of the schematic of the generated board.
defn view (def:LandPattern, view-mode:ViewMode) -> Null
defn view (def:SchematicSymbol, view-mode:ViewMode) -> Null
defn view (def:Instantiable, view-mode:ViewMode) -> Null
defn view (def:LandPattern) -> Null
defn view (def:SchematicSymbol) -> Null
defn view (def:Instantiable) -> Null
View a landpattern, schematic symbol, or pcb-component
.
Syntax
; Default: update the view
view(ocdb/utils/symbols/resistor-sym)
; Visualize, panning to the center of the view and zooming to fit
view(ocdb/utils/symbols/resistor-sym, NewView)
; Visualize, but maintain the same pan/zoom state.
view(ocdb/utils/symbols/resistor-sym, UpdateView)
Description
The view
command is used to visualize generated landpatterns,
schematic symbols, or components. It will fail if passed a pcb-module
or
array of instantiables.
defn view-design-explorer () -> Null
defn view-design-explorer (def:Instantiable -- default-3d:True|False = ?) -> Null
Visualize the design as a hierarchical tree.
Syntax
; Visualize the design as a tree
view-design-explorer()
Description
The view-design-explorer
command is used to visualize the design in the VSCode UI.
defn set-current-design (name:String) -> Null
Set the directory "./designs/
Syntax
set-current-design("jitx-design")
Description
The set-current-design
command will clear design state and
create a new design directory to store data JITX uses.
This directory will contain export data, 3D model files,
and cached data used by the design.
defn export-cad (field-mapping:False|Tuple<KeyValue<String, String>>) -> Null
defn export-cad ()
Export the JITX design to CAD.
Syntax
set-current-design("jitx-design")
set-export-backend(`altium)
; ... run design ...
export-cad()
; Cad files written to <project-root>/designs/jitx-design/altium
Or
val mappings = [
"rated-temperature" => "Rated-temperature"
"datasheet" => "Datasheet"
]
export-cad(mappings)
; Cad files exported with the field/property names converted.
Description
The export-cad
command will take the generated board and
schematic and convert it to a set of files in the design
directory that can be used in CAD software.
The output directory will be <design-directory-name>/<export-backend>
.
An optional field-mappings
can be supplied as an argument.
Name
, Description
, Manufacturer
, MPN
, Reference-prefix
will always be exported,
even if there is no field-mappings
.
Their exported names can be changed by using the field-mapping
.
Component properties can also be exported if an entry exists in field-mappings
.
For example, if there is a property(U1.datasheet) = "http://www.somewhere.com/my-datasheet.pdf"
,
using "datasheet" => "Datasheet"
in the field-mappings
(as above), a field
"Datasheet"
with value "https://www.somewhere.com/my-datasheet.pdf"
will appear as a property
of the component in CAD software.
A property will not be exported if...
- there is no corresponding entry in
field-mappings
; OR - it does not exist; OR
- its value cannot be converted to a string.
To export a property without name change, put the same name in both ends of
the mapping. For example, "rated-voltage" => "rated-voltage"
.
defn set-export-backend (backend:Symbol) -> Null
Set the CAD exporter backend to use.
Syntax
set-current-design("jitx-design")
set-export-backend(`altium)
; ... run design ...
export-cad()
; Cad files written to <project-root>/designs/jitx-design/altium
Description
The set-export-backend
command will control which CAD backend that export-cad
will use.
The backend will also define some contraints on internals like placement and visualization.
defn set-export-board? (b:True|False) -> Null
Set whether export-cad
should generate board files.
Syntax
set-export-board?(true) ; or false
Description
By default, the export command will generate a board. Use set-export-board?
to control
whether it is generated or not.
defn set-export-schematic? (b:True|False) -> Null
Set whether export-cad
should generate schematic files.
Syntax
set-export-schematic?(true) ; or false
Description
By default, the export command will generate a schematic. Use set-export-schematic?
to control
whether it is generated or not.
defn export-bom () -> Null
Export the bill of materials (BOM) to the design directory.
Syntax
set-bom-vendors([
"JLCPCB",
"Arrow",
"Avnet",
"DigiKey",
"Future",
"Mouser",
"Newark"
])
set-bom-design-quantity(100)
;write the BOM files to disk
export-bom()
Description
The export-bom
command will create the following files in the
design directory :
<design-directory>/bom/<design>.tsv
<design-directory>/bom/<design>.select
The .tsv
file is a tab separated value that can be imported by
spreadsheet software to order parts. The formatting is not defined
and subject to change. Parts are autoselected, unless overridden by the
.select
file.
defn view-bom (view:BOMViewMode = ?)
View the bill of materials (BOM).
Syntax
view-bom(view:BOMViewMode = BOM-LAST)
Description
BOMViewMode is either BOM-STD: evaluate based on the criteria and display the result BOM-DIFF: evaluate based on the criteria and display the difference since the last version BOM-LAST: do not evaluate, just display the last version if no parameter is given, view-bom(BOM-LAST) is the default.
defn set-bom-columns (cols:Tuple<BOMColumn>|False)
set-bom-columns(cols: Tuple
set the columns of the bill of materials (BOM).
Syntax
set-bom-columns([
BOMColumn(BOMFieldStatus, "Status", 10.0)
BOMColumn(BOMFieldInsts, "References", 10.0)
BOMColumn(BOMFieldMPN, "MPN", 10.0)
BOMColumn(BOMFieldDescription, "Description", 20.0)
BOMColumn(BOMFieldVendor, "Supplier", 10.0)
BOMColumn(BOMFieldQuantity, "Qty", 5.0)
BOMColumn(BOMFieldSubtotal, "Total", 5.0)
BOMColumn(BOMFieldPreferred, "Preferred", 5.0)
])
Description
Set the columns to display in BOM Visualizer and to export. If none is set, JITX default columns are used.
defn set-bom-metrics (metrics:Tuple<BOMMetric>|False)
set-bom-metrics(metrics: Tuple
set the metrics of the bill of materials (BOM).
Syntax
set-bom-metrics([
BOMMetric(BOMMetricLineItems, "Line Items"),
BOMMetric(BOMMetricComponentCount, "Components"),
BOMMetric(BOMMetricTotalCost, "Cost")
])
Description
Set the metrics and their display names in the BOM visualizer.
If not set, the above metrics are used by default.
defn set-bom-design-quantity (quantity:Int) -> Null
Set the quantity of boards to manufacturer when generating a BOM.
Syntax
set-bom-vendors([
"JLCPCB",
"LCSC",
"DigiKey",
"Future",
"Mouser",
"Arrow",
"Avnet"
"Newwark"
])
set-bom-design-quantity(100)
;write the BOM files to disk
export-bom()
Description
The set-bom-design-quantity
is used to define the number of boards
to manufacturer. It is combined with the vendors list to automatically
find parts from the vendors lists in the appropriate quantity to assemble
the generated board design.
defn set-paper (paper:Paper) -> Null
Set the paper size used in schematic export
Syntax
set-paper(ANSI-A4)
Description
The set-paper
command is used to define the paper size
in the exported schematic files.
Valid values are :
public defenum Paper :
ANSI-A0
ANSI-A1
ANSI-A2
ANSI-A3
ANSI-A4
ANSI-A5
ANSI-A
ANSI-B
ANSI-C
ANSI-D
ANSI-E
defn set-value-format-options (format-options:Tuple<String>) -> Null
Set the formats of the value strings during export operation.
Syntax
set-current-design("jitx-design")
set-export-backend(`kicad)
val format-options = ["keep-decimal" "LC-units-always-present"]
set-value-format-options(format-options)
; ... run design ...
export-cad()
; Cad files written to <project-root>/designs/jitx-design/altium
Description
The set-value-format-options
command will set the preferences to generate component values.
It is optional. export-cad
will work even if this command is not present.
By default, without this command the output will look like these:
By way of examples:
| |Value |Output|
|----------|------------------|------|
|Resistor |3300 Ohm |3K3 |
| |100 Ohm |100R |
|----------|------------------|------|
|Capacitor |0.0000047 Farad |4.7u |
| |0.000000010 Farad |10n |
|----------|------------------|------|
|Inductor |0.010 Henry |10m |
If a string is present in the format-options
, the value will be different:
"use-small-k" => 3300-ohm Resistor Value will be: 3k3
"use-greek-mu" => 0.0000047-farad Capacitor Value will be: 4.7μ
"use-greek-ohm" => 100-ohm Resistor Value will be: 100Ω
"R-unit-always-present" => 3300-ohm Resistor Value will be: 3K3R or 3K3Ω
"LC-units-always-present" => 0.0000047-farad Capacitor Value will be: 4.7uF
"R-not-replace-decimal-by-quantifier" => 3300-ohm Resistor Value will be: 3.3K
"LC-replace-decimal-by-quantifier" => 0.0000047-farad Capacitor Value will be: 4u7
"keep-decimal" => 100-ohm Resistor Value will be: 100.0R
"C-replace-n-by-K" => 0.000000010-farad Capacitor Value will be: 10K or 10k (if "use-small-k" is also present)
"add-tolerance" => 0.010-henry Inductor Value will be: 10m ± 20% (depends on actual tolerance value)
defn set-use-layout-sketch () -> Null
Configure the placer to run in sketch mode.
Syntax
set-use-layout-sketch()
Description
When running in sketch mode, the placer will not take Layout Groups into account (faster).
defn set-use-layout-groups () -> Null
Configure the placer to run in groups mode.
Syntax
set-use-layout-groups()
Description
When running in groups mode, the placer will take Layout Groups into account (slower).
defn enable-debug-plugin-serializer () -> Null
defn enable-spy () -> Null
Dump the generated internal representation during compilation
Syntax
enable-spy()
evaluate(my-design)
Description
The enable-spy
command is a debugging tool to dump the compiler's
internal representation(s) into a human readable text form. It
is generally only useful for analyzing internal compiler errors.
defn disable-spy () -> Null
Disable dumping the generated internal representation during compilation
Syntax
disable-spy()
Description
The disable-spy
command disables the dumping of the compiler's
internal representation(s) after a call to enable-spy
.
defn enable-import-dump-ir (b:True|False) -> Null
Dump the generated internal representation during import
Syntax
enable-import-dump-ir(true)
import-cad(...)
Description
The enable-import-dump-ir
command is a debugging tool to dump the importer's
internal representation(s) into a human readable text form. It
is generally only useful for analyzing importer errors. The command can be
called with false as the input to re-disable dumping after enabling.
defn optimized-mode? () -> True|False
Checks whether the algorithmic core has been compiled with optimizations enabled.
Syntax
optimized-mode?()
Description
The optimized-mode?
command returns true
if the algorithmic
core has been compiled with optimizations enabled, or false
otherwise.
defn import-cad (input-dir:String, output-dir:String, cad:CadSoftware) -> ImportCadResult
defn import-cad (input-dir:String, output-dir:String, cad:CadSoftware, field-mapping:False|Tuple<KeyValue<String, String>>) -> ImportCadResult
Import CAD projects from KiCAD or Altium.
Syntax
defpackage import-script :
import core
import jitx
import jitx/commands
import-cad("input-directory", "output-directory", Altium)
Optional field-mapping
can be used to change property field names, used only in KiCad.
Possible target Fields are "Name", "Manufacturer", "MPN", "Description", case-sensitive.
For example, you can map "Field1" to "Manfacturer" and "Part Desc" to "Description" by
val mapping = [ "Field1" => "Manufacturer" "Part Desc" => "Description" ]
import-cad("input-design", "output-directory", Altium, field-mapping)
Description
The import-cad
command is used to read KiCAD or Altium files from input-directory
and generate JITX code in output-directory
. The currently supported CadSoftware
values are :
public defenum CadSoftware :
Kicad ; Experimental v6 support
Altium
Requirements
- The
input
directory must exist. input
may not be a symbolic link.- The
output
directory must not exist or be an empty directory - When importing a board, schematic, and netlist:
- Reference designators in the project files must be consistent.
- Nets on the board, in the schematic, and netlist must be consistent.
- "Consisent" means components may not be present on the board or schematic that aren't in the other, nets on the board/schematic/netlist must be the same, etc.
- Altium constraints :
- Only files generated with the JITX Altium Extension will be imported. These are files with the following extensions :
(SCHLIB).JSON
(SCHDOC).JSON
(PRJPCB).JSON
(PCBLIB).JSON
(PCBDOC).JSON
- Only files generated with the JITX Altium Extension will be imported. These are files with the following extensions :
- Kicad constraints :
- Only files of the following file extension will be imported :
kicad_mod
kicad_sym
kicad_pcb
kicad_sch
net
kicad_pro
fp-lib-table
- Only Kicad 6 is supported.
- A netlist (
.net
) file must be provided. - Only one
.kicad_pcb
,.kicad_pro
, and.net
file may exist in theinput
directory.
- Only files of the following file extension will be imported :
- 3D Model Files:
- only
.step
,.stp
, and.wrl
files will be copied to imported code.
- only
- BOM Files:
- BOM files must be named
jitx-import-bom.tsv
and be of the correct schema
- BOM files must be named
defn min-space (s:Seqable<Shape>) -> Double
Compute the minimum space in a sequence of shapes.
Syntax
val shapes = [
loc(1.0, 1.0) * Circle(0.001)
loc(2.0, -1.0) * Circle(0.01)
]
println(min-space(shapes))
Description
The minimum space in a set of shapes is defined as the smallest distance between non overlapping sides of the shapes. This command is useful when writing DRC checks.
defn min-width (s:Seqable<Shape>) -> Double
Compute the minimum width in a sequence of shapes.
Syntax
val r1 = loc(-0.5, 0.0) * Rectangle(2.0, 1.0)
val r2 = loc( 0.5, 0.0) * Rectangle(2.0, 1.0)
val mw = min-width([r1, r2])
println(mw)
Description
The minimum width is defined as the minimum length of a line you can draw inside the shape between two segments (on the boundary) with opposite directions and overlapping.
defn offset (s:Shape, amount:Double) -> Shape
Compute a shape that is "offset" from another by a fixed distance.
Syntax
val rect = Rectangle(10.0, 10.0)
val offset-rect = offset(rect, 1.0)
println(offset-rect) ; Rectangle(11.0, 11.0)
Description
The offset
command derives a new shape from an existing
one using a factor to expand the dimensions in x/y directions.
This is useful when creating new shapes that are a fixed size
larger than some root shape.
defn dims (s:Shape) -> Dims
Compute the x/y dimensions of a shape.
Syntax
val shape = Union([
Rectangle(10.0, 10.0)
loc(2.0, 0.0 * Circle(10.0, 10.0)
])
println(dims(shape))
Description
The dims
command takes a shape as an argument and computes the
x/y dimensions (bounding box) of the complete shape. This is useful
for simple collision detection and size computation.
defn expand (s:Shape, amount:Double) -> Shape
Expand a shape by a fixed distance.
Syntax
val shape = Rectangle(10.0, 10.0)
val expanded = expand(shape, 1.0)
println(expanded)
Description
The expand
command computes an "expanded" shape, which
is a shape that is exactly amount
distance away from the
perimeter of s
.
To use these functions, import the package jitx/commands
and the package jitx
where the enums are defined.
import jitx
import jitx/commmands
defn ref (a:JITXObject) -> Ref
Get the associated Ref
of an object instance.
Syntax
pcb-module my-module :
inst U1 : my-component
; Print out the Refs of the pins of a component
for p in pins(U1) do :
println(ref(p))
Description
Instances, ports, pins, and pads in the design hierarchy have
an associated "reference" name, for example, my-module.U1.p[3]
.
The ref(...)
introspection will look up the associated Ref
value
with any object, if it is assigned.
defn name (def:JITXDef) -> String
Get the name of an object
Syntax
pcb-component my-component :
name = "My Awesome Component"
println(name(my-component))
; prints, "My Awesome Component"
Description
Every definition in JITX has an associated name. It may
be overridden explicitly using the name = ...
syntax,
else it is inferred from the identifier given in the definition.
The name (...)
introspection function will lookup the associatd
name from a given definition
defn evaluate (def:JITXDef) -> Null
Evaluates a definiton, expanding its statements.
Syntax
pcb-pad simple-pad :
shape = Circle(1.0)
apply-soldermask()
evaluate(simple-pad)
Description
The evaluate (...)
introspection forces the expansion
of a pcb-***
definition, including all of its generators. This
is useful when writing unit test, or working on generators outside
of an existing design.
defn port-type (obj:JITXObject) -> PortType
Get the type of a port from a component or module definition/instance
Syntax
pcb-module my-module :
port p : i2c
println(port-type(p))
Description
Ports of components or modules may be one of a given [PortType], valid port types are SinglePin, Bundle, PortArray
defn instantiable-type (obj:JITXObject) -> InstantiableType
Return the type of instantiable an object is.
Syntax
pcb-module my-module :
...
; prints `Instantiable`.
println(instantiable-type(obj))
Description
The instantiable-type
is either a single instantiable (Instantiable
) or
an array of instantiables (InstantiableArray
).
defn indices (obj:JITXObject) -> Seqable<Int>
Get the indices of an array of JITX objects.
Syntax
inside pcb-module :
port p: pin[[1 2 3 5 7]]
val indices = indices(p)
for idx in indices do :
val p* = p[idx]
println(port-type(p*))
Description
Arrays of JITX objects can have custom indices. The indices (...)
introspection
is useful for extracting port/pin or instances indices for generators that may
need to reference them.
defn instances (obj:JITXObject)
defn instances (def:Instantiable)
Get the instances of a module.
Syntax
inside pcb-module :
for r in [1.0e3, 2.0e3, 3.0e3] do :
inst R : gen-res-cmp(r)
for i in instances(self) do :
if has-property?(i.resistance) :
println("There is a resistor: %_ ohms." % [property(i.resistance)])
Description
The instances
introspection returns a sequence of all the instances within
a module. This is useful for checks and generators that need to loop over
the instances of a module to find their properties, generate additional
instances, and so on.
defn public-instances (obj:JITXObject)
defn public-instances (def:Instantiable)
Get the public instances of a module.
Syntax
pcb-module my-module :
inst R1 : gen-res-cmp(1.0e3)
public inst R2 : gen-res-cmp(2.0e3)
; This loop will only print "2000.0", since
; R1 is a private instance.
for cmp in public-instances(my-module) do :
println(property(cmp.resistance))
Description
By default, all instances in a module are considered to have "private" visibility, which disables instantiators from referencing the instances' refs, ports, and properties.
The public-instances
introspection returns a sequence of instances of a module that
are explicitly marked public
.
defn single-instances (obj:JITXObject)
defn single-instances (def:Instantiable)
Get the single (non array) instances of a module.
Syntax
pcb-module my-module :
inst R : gen-res-cmp(1.0e3)[10]
inst R11 : gen-res-cmp(2.0e3)
; This loop will only print "2000.0", as there
; is only one "single' instance in `my-module`.
for cmp in single-instances(my-module) do :
println(property(cmp.resistance))
Description
The single-instances
introspection returns a sequence of instances
of a module that are not defined within instance arrays.
defn public-single-instances (obj:JITXObject)
defn public-single-instances (def:Instantiable)
Get the public single (non array) instances of a module.
Syntax
pcb-module my-module :
inst R : gen-res-cmp(1.0e3)[10]
inst R11 : gen-res-cmp(2.0e3)
public inst R12 : gen-res-cmp(3.0e3)
; This loop will only print "3000.0", as there
; is only one "single' instance in `my-module`.
; that is explicitly marked `public`
for cmp in single-instances(my-module) do :
println(property(cmp.resistance))
Description
The public-single-instances
introspection returns a sequence
of explicitly public instances that are not defined within instance
arrays.
defn component-instances (obj:JITXObject)
defn component-instances (def:Instantiable)
Get the instances of a module that are components (not modules).
Syntax
pcb-module sub-module :
..
pcb-module my-module :
inst sub : sub-module
inst cmp : gen-res-cmp(1.0e3)
; This will only print "my-module.cmp" as it is the only
; instance of the module that is **not** another module.
for cmp in component-instances(my-module) do :
println(ref(cmp))
Description
The component-instances
introspection returns a sequence of
instances of a module that are components, that is to say that
they are instances at the bottom of the design hierarchy (not
contained within submodules of the module).
defn ports (obj:JITXObject)
defn ports (def:Instantiable|Bundle)
Get the ports of a component, bundle, or module
Syntax
val ports = ports(my-component)
for p in ports do :
println(ref(port))
Description
ports
are defined using the port
statement. The ports
introspection
retrieves all port
statements associated with the bundle, component, or module.
defn pins (obj:JITXObject)
defn pins (def:Instantiable|Bundle|SchematicSymbol)
Get the pins associated with an object.
Syntax
val cmp = gen-res-cmp(1.0e3)
for p in pins(cmp) do :
println(ref(p))
Description
The pins
introspection is useful for extracting the pins of a component,
bundle, or schematic symbol.
defn nets (obj:JITXObject)
defn nets (def:Instantiable)
Get the nets of a module.
Syntax
pcb-module my-module :
inst R1 : gen-res-cmp(1.0e3)
inst R2 : gen-res-cmp(1.0e3)
net my-net (R1.p[1], R2.p[1])
for n in nets(self) do :
println(name(n))
Description
The nets
introspection is useful for extracting all the net
statements of
a module. It's important to note that this corresponds to the individual net
statements, and not the electrical concept of a "net." For example, if there are
two net
s in a module that are electrically identical, calling nets(self)
will
return both of them.
defn public-nets (obj:JITXObject)
defn public-nets (def:Instantiable)
Get the nets of a module.
Syntax
pcb-module my-module :
inst R1 : gen-res-cmp(1.0e3)
inst R2 : gen-res-cmp(1.0e3)
net my-net (R1.p[1], R2.p[1])
public net my-public-net (R1.p[2], R2.p[2])
for n in public-nets(self) do :
println(name(n))
Description
The public-nets
introspection is useful for extracting all the net
statements of
a module that are explicitly marked public
. It's important to note that this corresponds
to the individual net
statements, and not the electrical concept of a "net." For example,
if there are two public net
s in a module that are electrically identical, calling nets(self)
will return both of them.
defn single-pin-nets (obj:JITXObject)
defn single-pin-nets (def:Instantiable)
Get the nets of a module.
Syntax
pcb-module my-module :
inst R1 : gen-res-cmp(1.0e3)
inst R2 : gen-res-cmp(1.0e3)
net my-net (R1.p[1], R2.p[1])
net my-single-net (R1.p[2])
for n in public-nets(self) do :
println(name(n))
Description
The single-pin-nets
introspection is useful for extracting all the net
statements of
a module that contain only a single pin.
defn public-single-pin-nets (obj:JITXObject)
defn public-single-pin-nets (def:Instantiable)
Get the nets of a module.
Syntax
pcb-module my-module :
inst R1 : gen-res-cmp(1.0e3)
inst R2 : gen-res-cmp(1.0e3)
net my-net (R1.p[1], R2.p[1])
public net my-public-net (R1.p[2], R2.p[2])
for n in public-nets(self) do :
println(name(n))
Description
The public-single-pin-nets
introspection is useful for extracting all the net
statements of
a module with a single pin that are explicitly marked public
.
defn topology-ends (obj:JITXObject) -> KeyValue<JITXObject, JITXObject>
Get the ends of a named topology.
Syntax
pcb-module my-module :
inst R1 : gen-res-cmp(1.0e3)
inst R2 : gen-res-cmp(1.0e3)
topology my-topo (R1.p[1] => R2.p[1])
val ends:KeyValue<JITXObject,JITXObject> = topology-ends(my-topo)
println("Topology goes from %_ to %_" % [key(ends) value(ends)])
Description
The topology-ends
introspection is useful for extracting the start and end
of a named topology object.
defn refs (obj:JITXObject)
Return all the refs
inside of an object
Syntax
pcb-module main-module :
for r in refs(self) do :
println(r)
defn get-net (name:String) -> JITXObject
Find a net with a given name in a module.
Syntax
pcb-module my-module :
inst R1 : gen-res-cmp(1.0e3)
inst R2 : gen-res-cmp(1.0e3)
net my-net (R1.p[1], R2.p[1])
; ...
val my-net = get-net(my-module, "my-net")
Description
Sometimes nets are generated programatically and it may not be possible
to use the local identifier to lookup a net. For this purpose, the
get-net
introspection will search the net statements of the current
module.
This function must be called inside pcb-module
.
defn length (obj:JITXObject) -> Int
Get the length of an instance or bundle array.
Syntax
inst R : gen-res-cmp(1.0e3)[5]
println(length(R))
Description
Bundles and instances may be created in arrays, the length
introspection
will return the length of those arrays.
defn originating-instantiable (obj:JITXObject) -> Instantiable
Get the inst
definition that created an instance.
Syntax
inst i: gen-res-cmp(3.0)
print-def(originating-instantiable(i))
Description
The originating-instantiable
retrieves the Instantiable
that was used to create
an instance in a module.
defn containing-instance (obj:JITXObject) -> Instance|False
Get the instance that an object is contained within, if it exists.
Syntax
pcb-module sub-module :
public inst R : gen-res-cmp(1.0e3)
pcb-module parent-module :
inst i: sub-module
println(ref(containing-instance(i.R)))
Description
The containing-instance
introspection retreives the instance that
owns another instance. This is useful for generators and checks that
might not have access to the original identifier of a given instance.
defn instance-definition (obj:JITXObject) -> Instantiable
Get the pcb-*
definition of an instance.
Syntax
pcb-module my-module :
inst X : my-component
print-def(instance-definition(X))
Description
The instance-definition
introspection retrieves the pcb-*
definition
that corresponds to a given component or module.
defn instance-type (obj:JITXObject) -> InstanceType
Determine if an instance is a component, module, or array.
Syntax
pcb-module my-module :
inst X : my-component[10]
print-def(instance-type(X))
Description
The instance-type
introspection retrieves the type of an instance, represented
as the enum InstanceType
:
public defenum InstanceType :
SingleComponent
SingleModule
InstanceArray
defn mpn? (c:Instantiable) -> String|False
defn mpn? (obj:JITXObject) -> String|False
Get the MPN of a component, if it exists.
Syntax
pcb-module my-module :
inst U : ocdb/components/abracon/AMB7/component(10.0e6)
println(mpn?(U))
Description
Looks up the value associated with an mpn = ...
statement in a
pcb-component.
defn manufacturer? (c:Instantiable) -> String|False
defn manufacturer? (obj:JITXObject) -> String|False
Get the manufacturer name of a component, if it exists.
Syntax
pcb-module my-module :
inst U : ocdb/components/abracon/AMB7/component(10.0e6)
println(manufacturer?(U))
Description
Looks up the value associated with an manufacturer = ...
statement in a
pcb-component.
defn datasheet? (c:Instantiable) -> String|False
defn datasheet? (obj:JITXObject) -> String|False
Get the datasheet name of a component, if it exists.
Syntax
pcb-module my-module :
inst U : ocdb/components/abracon/AMB7/component(10.0e6)
println(datasheet?(U))
Description
Looks up the value associated with an datasheet = ...
statement in a
pcb-component.
defn reference-prefix? (c:Instantiable) -> String|False
defn reference-prefix? (obj:JITXObject) -> String|False
Get the reference prefix of a component, if it exists.
Syntax
pcb-module my-module :
inst U : ocdb/components/abracon/AMB7/component(10.0e6)
println(reference-prefix?(U))
Description
Looks up the value associated with an reference-prefix = ...
statement in a
pcb-component.
defn emodel? (c:Instantiable) -> EModel|False
defn emodel? (obj:JITXObject) -> EModel|False
Get the emodel of a component or the component of an inst, if it exists.
Syntax
pcb-module my-module :
inst U : chip-resistor(1000.0)
println(emodel?(U))
Description
Looks up the value associated with an emodel = ...
statement in a
pcb-component.
defn reference-designator (obj:JITXObject) -> String
Get the reference designator of a component.
Syntax
pcb-module my-module :
inst R : gen-res-cmp(1.0e3)
println(reference-designator(R))
Description
The reference-designator
retrieves the the reference designator of
a component in the design, which is either generated automatically or
defined explicitly using the reference-designator(object) = ...
syntax.
defn connected? (obj:JITXObject) -> True|False
defn connected? (objs:Seqable<JITXObject>) -> True|False
Check if a pin or a net is connected
Syntax
pcb-module my-module :
inst R : gen-res-cmp(1.0e3)
println(connected?(R.p[1]))
Description
The connected?
introspection checks if a pin or net is connected
to any other pins or nets in a design. This is useful when writing checks
for connected or unconnected components.
defn connected-pins (obj:JITXObject) -> Tuple<Pin>
Return the pins connected to an existing pin.
Syntax
pcb-module my-module :
pin gnd
public inst R: gen-res-cmp(1.0e3)[4]
for n in 0 to 4 do :
net (gnd, R[n].p[1])
val connected = connected-pins(gnd)
for p in connected do :
println(ref(p))
Description
The connected-pins
introspection returns a Tuple
of all the pins
connected to the argument, excluding the argument pin itself. This is
useful for writing connectivity checks.
defn all-connected-items (m:Self|Instantiable) -> Tuple<ConnectedItems>
Returns a list of all the connected items to a pin or net.
Syntax
pcb-module my-module :
val N = 100
inst R: gen-res-cmp(1.0e3)[N]
net gnd ()
for n in 0 to 100 do :
net (R.p[1], gnd)
val connected-items = all-connected-items(self)
for item in connected-items :
for n in nets(item) do :
println(ref(n))
Description
The all-connected-items
introspection returns a tuple of ConnectedItems
which represent all the connected items in a module. This is primarily
used when writing checks that need to observe the full design, for example
checking if specific component pins are net'd together or pins on nets have
the appropriate properties.
The ConnectedItems
structure contains the following fields :
public defstruct ConnectedItems :
nets: Tuple<Net>
module-pins: Tuple<Pin>
component-pins: Tuple<Pin>
abstract-pins: Tuple<Pin>
defn layers (obj:LandPattern|Pad|Self) -> Tuple<LayerShape>
Get the layer
statements of a pcb-landpattern
or pcb-pad
.
Syntax
val landpattern = ocdb/components/st-microelectronics/landpatterns/TSSOP20
for layer in layers(landpattern) do :
println("There is a %_ on %_." % [shape(layer), specifier(layer)])
Description
The layers
introspection finds all the geometry specified in layer
statements
in landpattern or pad definitions. It is useful for writing landpattern checks.
defn layer (obj:LandPattern|Pad|Self, l:LayerSpecifier) -> Tuple<Shape>
Get all the shapes on a specific layer of a landpattern
Syntax
val landpattern = ocdb/components/st-microelectronics/landpatterns/TSSOP20
val solder-mask = layer(landpattern, SolderMask(Top))
Description
The layer
introspection retrieves all the geometry on a single layer defined in a landpattern or pad.
defn model3ds (obj:LandPattern|Self) -> Tuple<Model3D>
Get the model3ds of a pcb-landpattern
.
Syntax
val landpattern = ocdb/components/st-microelectronics/landpatterns/TSSOP20
for model3d in model3ds(landpattern) do :
println(model3d)
Description
The model3ds
introspection returns all the Model3D
s in the landpattern.
defn pads (obj:LandPattern|Self) -> Tuple<LandPatternPad>
Get the pads of a pcb-landpattern
.
Syntax
val landpattern = ocdb/components/st-microelectronics/landpatterns/TSSOP20
for pad in pads(landpattern) do :
println(pad)
Description
The pads
introspection returns all the LandPatternPad
s that have
been created using the pad ...
statement.
defn pose (l:JITXObject)
Get the pose
of a pad inside a pcb-landpattern
Syntax
val landpattern = ocdb/components/st-microelectronics/landpatterns/TSSOP20
for pad in pads(landpattern) do :
println("There is a pad at: %_" % [pose(pad)])
Description
The pose
introspection retrieves the Pose
of a pad in a landpattern.
defn side (l:JITXObject)
Get the side of a pad in a landpattern
Syntax
val landpattern = ocdb/components/st-microelectronics/landpatterns/TSSOP20
val pads-on-top =
for pad in pads(landpattern) filter :
side(pad) is Top
Description
The side
introspection is used to find which side of the board a
landpattern pad is upon.
defn pad (l:JITXObject)
Get the pcb-pad
definition of a pad in a landpattern.
Syntax
val landpattern = ocdb/components/st-microelectronics/landpatterns/TSSOP20
for p in pads(landpattern) do :
val def = pad(p)
print-def(def)
Description
The pad
introspection returns the pcb-pad
definition of a
landpattern pad. This is useful for writing landpattern checks that
need to inspect the pad data, for example their layers.
defn pad-shape (p:Pad|Self)
Get the shape of a pcb-pad
definition.
Syntax
pcb-pad my-pad :
shape = Circle(1.0)
type = SMD
println(pad-shape(my-pad))
Description
The pad-shape
introspection returns the shape of a
pad that has been defined using the shape = ...
statement.
defn pad-type (p:Pad|Self)
Get the type of a pcb-pad
definition.
Syntax
pcb-pad my-pad :
shape = Circle(1.0)
type = SMD
println(pad-type(my-pad))
Description
The pad-type
introspection returns the PadType
associated with a pcb-pad
definition, which
can either be TH
(through hole) or SMD
(surface mount)
defn landpattern (obj:Self|JITXDef) -> LandPattern
Get the landpattern associated with a pcb-component
definition.
Syntax
val component = ocdb/components/abracon/AMB7/component(10.0e6)
val landpattern = landpattern(component)
print-def(landpattern)
Description
The landpattern
introspection function returns the pcb-landpattern
definition associated with
a pcb-component
definition. This is useful for writing checks that look at the landpatterns
of components placed in the design.
defn package-poses (obj:JITXDef) -> Tuple<PackagePose>
Get the package poses associated with a pcb-component
definition.
Syntax
val package-poses = package-poses(my-module)
do(println, package-poses)
print-def(inst(package-poses[0]))
Description
The package-poses
introspection function returns the pose information given explicitly to instances in a pcb-module
definition.
The returned object is
public defstruct PackagePose :
inst:Instance
pose:Pose|False
side:Side
anchor:Instance|False
defn do-not-populate? (obj:JITXObject) -> True|False
Check if a component instance is marked do-not-populate
defn component-status? (obj:JITXObject) -> [ComponentBOMStatus, ComponentBoardStatus]
Retrieve the component in-BOM and on-board status of a component instance (deprecated)
defn instance-status? (obj:JITXObject) -> [ComponentBOMStatus, ComponentBoardStatus]
Retrieve the component in-BOM and on-board status of a component instance (deprecated)
defn no-connect? (pin:JITXObject) -> True|False
Check if a pin is no-connect.
Syntax
pcb-component component :
pin GND
pin VDD
pin NC
no-connect(NC)
pcb-module my-module :
inst U1 : component
; prints false.
println(no-connect?(U1.GND)
; prints false.
println(no-connect?(U1.VDD)
; prints true.
println(no-connect?(U1.NC)
Description
The no-connect?
introspection checks if a no-connect(...)
statement has been applied
to the given pin. This is useful when writing checks.
defn get-min-width (l:LayerSpecifier|LayerIndex) -> Double
Get the minimum width rule for a given LayerSpecifier
.
Syntax
val min-cu-width = get-min-width(LayerIndex(0))
Description
get-min-width
is a convenience function around clearance
and current-rules
introspections
for quickly retrieving the DRC parameters for minimum width on a given layer. Currently, only
copper, silkscreen, and solder mask opening widths are supported.
defn get-min-space (l:LayerSpecifier|LayerIndex) -> Double
Get the minimum space rule for a given LayerSpecifier
.
Syntax
val min-cu-space = get-min-space(LayerIndex(0))
Description
get-min-space
is a convenience function around clearance
and current-rules
introsepctions
for quickly retrieving the DRC parameters for the minimum space on a given layer. Currently only
copper and solder mask bridge spacings are supported.
defn get-board () -> Board|False
Get the current pcb-board
, if it exists
Syntax
val current-board? = get-board()
Description
The get-board
introspection returns a board that has been set by set-board
, if it exists.
defn has-board? () -> True|False
Check if the board has been set.
Syntax
println(has-board?())
set-board(ocdb/utils/defaults/default-board(4))
println(has-board?())
Description
The has-board?
introspection is a convenience function to check if the board has been set by
a call to set-board
.
defn current-rules () -> Rules
Get the current design rules of the project
Syntax
val rules = current-rules()
Description
The current-rules
introspection returns the design rules that
have been set with set-rules
, or set-default-rules
if set-rules
has never been called. It fails if there are no design rules for the design.
defn current-rules? () -> Rules|False
Get the current design rules, if they exist.
Syntax
val no-rules = current-rules?()
println(no-rules) ; false
set-rules(ocdb/utils/defaults/default-rules)
val rules = current-rules?()
println(rules) ; Rules
Description
Like current-rules
, current-rules?
returns the design rules of the
project if they have been set, but does not crash if they are not available
and returns false
otherwise.
defn clearances (r:Rules) -> Tuple<RuleClearance>
Get the clearances specified by a Rules
object
Syntax
for clearance in clearances(current-rules()) :
println(clearance)
Description
The clearances
introspection returns the design rules as a list
of RuleClearance
objects, containing their name and value.
defn clearance (r:Rules, c:ClearanceType) -> Double
defn clearance (c:ClearanceType) -> Double
Get a specific clearance rule.
Syntax
val min-cu-width = clearance(current-rules(), MinCopperWidth)
println(min-cu-width)
; convenience variation to lookup a
; val min-cu-width = clearance(MinCopperWidth)
Description
The clearance
introspection takes a Rules
and ClearanceType
and
lookups up the associated value with that ClearanceType
. When called
without an explicit Rules
argument, the current-rules()
are used.
defn get-export-backend ()
Get the currently set CAD exporter backend.
Syntax
set-current-design("jitx-design")
get-export-backend()
Description
The get-export-backend
command returns which CAD backend export-cad
is currently set to use.
defn get-shape-from-contour (lines:Tuple<Line|Polyline>) -> Polygon|PolygonWithArcs
Get a shape from a tuple of lines/polylines.
Syntax
val line1 = Line(1.0, [Point(0.0, 1.0), Point(0.0, 0.0)])
val line2 = Polyline(1.0, [Point(0.0, 0.0), Point(1.0, 0.0), Point(0.0, 1.0)])
val shape = get-shape-from-contour([line1, line2])
println(shape)
Description
The get-shape-from-contour
command computes a polygon from
a list of lines and/or polylines. The lines/polylines must
be in the same order as they are along the boundary of the
polygon.
Part Query API
The part query API is a low level API that returns actual components that meet the specified criteria.
We recommend using the Higher level Part Query accessors to query for parts.
The JITX database currently supports 4 categories. Detailed information on how to query each category can be found at:
The API queries the JITX database containing millions of parts and supports:
- filtering on attributes by equality, inequality or set of values,
- optimizing on an attribute (for example smallest area or price),
- querying for a specific number of parts
- requiring some attributes to exist in the resulting parts (for example tolerance)
- listing the available values for an attribute after filtering on other query parameters
- integrate real-time sourcing data to the query
Part query results are cached in memory for future use. This can be particularly useful when using the jitx repl
.
The cache is lost when a jitx program exits.
Caching
Internet is required to perform the part queries the first time. All results are then cached to disk to be reused across sessions. To query fresh parts, clear the cache with:
stanza> import jitx/commands
stanza> clear-dbquery-cache()
Accessors
The part query API is imported from the package jitx/commands
. Here are the accessors:
public defn dbquery (params: Tuple<KeyValue<String, Tuple<String>|Tuple<Double>|String|Double|Int>>, limit: Int) -> Tuple<JSON>
The maximum number of components that can be queried at a time by dbquery
is 1000.
Any value of limit
in dbquery
above 1000 will throw an error.
public defn dbquery-all (params: Tuple<KeyValue<String, Tuple<String>|Tuple<Double>|String|Double|Int>>) -> Tuple<JSON>
dbquery-all
calls dbquery
with a limit
of 25.
As our part database contains millions of resistors and hundreds of thousands of inductors and capacitors,
dbquery-all
cannot return all components that meet the query.
public defn dbquery-first (args: Tuple<KeyValue<String, Tuple<String>|Tuple<Double>|String|Double|Int>>) -> JSON
dbquery-first
calls dbquery
with a limit of 1, if no component is found, it throws a NoComponentMeetingRequirements
Examples
Querying the smallest 1Ω resistor:
stanza> import jitx/commands
stanza> val resistor-json = dbquery-first(["category" => "resistor", "resistance" => 1.0, "_sort" => ["area"]])
stanza> println(resistor-json)
JObject(entries = ["_id" => "0f3e3eeb6393f83d591a03c3" "trust" => "low" "category" => "resistor" "mpn" => "CRCW01001R00FYEL" "mounting" => "smd" "manufacturer" => "Vishay Dale" "type" => "chip" "dimensions" => JObject(entries = ["x" => 0.4 "y" => 0.2 "z" => 0.15 "area" => 0.08]) "stock" => 9355.0 "minimum_quantity" => 1.0 "metadata" => JObject(entries = ["datasheets" => "https://www.vishay.com/docs/20056/crcw01005e3.pdf" "image" => "//media.digikey.com/Renders/Vishay%20Dale%20Renders/CRCW-01005-(0402metric).jpg" "digi-key-part-number" => "541-4281-6-ND" "description" => "RES SMD 1 OHM 1% 1/32W 01005" "factory-stock" => 0.0 "unit-price" => "Digi-Reel" "qty" => 0.0 "packaging" => "Digi-Reel®" "series" => "CRCW" "supplier-device-package" => 1005.0 "number-of-terminations" => 2.0]) "tolerance" => JObject(entries = ["min" => -0.01 "max" => 0.01]) "resistance" => 1.0 "rated-power" => 0.03 "composition" => "thick-film" "tcr" => JObject(entries = ["pos" => -0.0002 "neg" => 0.0006]) "rated-temperature" => JObject(entries = ["min" => -55.0 "max" => 125.0]) "case" => "01005" "update_date" => "2021-09-04T01:35:34.335000"])
stanza> import ocdb/utils/db-parts
stanza> import json
stanza> println $ Resistor(resistor-json as JObject)
Resistor(
mpn = CRCW01001R00FYEL
trust = low
(x, y, z) = (0.4, 0.2, 0.15)
mounting = smd
rated-temperature = MinMaxRange(min=-55.0, max=125.0)
case = 01005
type = chip
tolerance = MinMaxRange(min=-0.01, max=0.01)
resistance = 1.0
composition = thick-film
rated-power = 0.03
TCR = TCR(positive=-0.0002, negative=0.0006)
sourcing = ESR(price=false, minimum-quantity=1, stock=9355)
metadata =
"datasheets" => "https://www.vishay.com/docs/20056/crcw01005e3.pdf"
"image" => "//media.digikey.com/Renders/Vishay%20Dale%20Renders/CRCW-01005-(0402metric).jpg"
"digi-key-part-number" => "541-4281-6-ND"
"description" => "RES SMD 1 OHM 1% 1/32W 01005"
"factory-stock" => 0.0
"unit-price" => "Digi-Reel"
"qty" => 0.0
"packaging" => "Digi-Reel®"
"series" => "CRCW"
"supplier-device-package" => 1005.0
"number-of-terminations" => 2.0)
Querying some capacitors (at most 25 by default):
$ jitx repl
stanza> import jitx/commands
stanza> val capacitor-jsons = dbquery-all(["category" => "capacitor"])
Querying 200 inductors with 1µH inductance:
$ jitx repl
stanza> import jitx/commands
stanza> val inductor-jsons = dbquery(["category" => "inductor", "inductance" => 1.0e-6], 200)
Querying the list of available mcu cores in the JITX database:
$ jitx repl
stanza> import jitx/commands
stanza> val cores = dbquery-all(["category" => "microcontroller", "_distinct" => "core"])
stanza> println(cores)
["ARM Cortex-M0" "ARM Cortex-M0+" "ARM Cortex-M3" "ARM Cortex-M4" "ARM Cortex-M7"]
Querying the list of available packages for smd resistors:
$ jitx repl
stanza> import jitx/commands
stanza> println $ dbquery-all(["category" => "resistor" "mounting" => "smd", "_distinct" => "case"])
[JNull() "009005" "01005" "0201" "02016" "0202" "0302" "0303" "0402" "0404" "0503" "0505" "0603" "0612" "0805" "1206" "1210" "1218" "1812" "2-SMD, J-Lead" "2010" "2010 J-Lead" "2012 J-Lead" "2015" "2512"]
Creating a module with the cheapest smd resistor with a resistance of 1Ω±5%:
#use-added-syntax(jitx)
defpackage my-design :
import jitx/commands
pcb-module my-module :
inst resistor : dbquery-first(["resistance" => 10.0 "tolerance" => 0.05 "mounting" => "smd" "_sort" => ["cost"]])
Querying the list of packages available for mcus:
$ jitx repl
stanza> import jitx/commands
stanza> val packages = dbquery-all(["category" => "microcontroller" "_distinct" => "mfg-package"])
stanza> println(length(packages))
28
stanza> println(packages)
["LFBGA100" "LQFP100" "LQFP144" "LQFP176" "LQFP208" "LQFP48" "TFBGA216" "TFBGA225" "TFBGA64" "UFBGA100" "UFBGA132" "UFBGA169" "UFBGA64" "UFQFPN28" "UFQFPN32" "UFQFPN48" "WLCSP100" "WLCSP104" "WLCSP156" "WLCSP168" "WLCSP180" "WLCSP25" "WLCSP36" "WLCSP49" "WLCSP63" "WLCSP64" "WLCSP81" "WLCSP90"]
Querying the list of mcu mpns for package "WLCSP49":
$ jitx repl
stanza> import jitx/commands
stanza> val mpns = dbquery(["category" => "microcontroller" "mfg-package" => "WLCSP49" "_distinct" => "mpn"], 500)
stanza> println(length(mpns))
21
stanza> println(mpns)
["STM32F071CBY6" "STM32F071CBY7" "STM32F072CBY6" "STM32F072CBY7" "STM32F078CBY6" "STM32F303C8Y6" "STM32F318C8Y6" "STM32F334C8Y6" "STM32F334C8Y7" "STM32F411CCY6" "STM32F411CCY7" "STM32F411CEY6" "STM32F411CEY7" "STM32L071CBY6" "STM32L071CBY7" "STM32L071CZY6" "STM32L071CZY7" "STM32L073CZY6" "STM32L073CZY7" "STM32L082CZY6" "STM32L082CZY7"]
Querying the list for all bundles for all mcus with the package "WLCSP49":
$ jitx repl
stanza> import jitx/commands
stanza> val mcus = dbquery(["category" => "microcontroller" "mfg-package" => "WLCSP49"], 500)
stanza> println(length(mcus))
21
stanza> import json
stanza> println $ to-tuple $ to-hashset<String> $ for mcu in (mcus as Tuple<JObject>) seq-cat : mcu["bundles"] as Tuple<String>
["RCC_OSC32_OUT" "USART3_RTS" "I2S1_CK" "SPI1_SCK" "RCC_OSC_OUT" "USART4_RTS" "LPUART1_DE" "LPUART1_CTS" "SPI2_SCK" "USART5_RTS" "SPI3_SCK" "SPI4_SCK" "LPUART1_TX" "SPI5_SCK" "SYS_JTDO-SWO" "I2S5_SD" "LPUART1_RX" "I2S4_SD" "I2S3_SD" "I2C1_SDA" "I2S2_SD" "I2C2_SDA" "I2S1_SD" "I2C3_SDA" "SYS_SWCLK" "SYS_JTDO-TRACESWO" "SPI5_MISO" "SPI5_MOSI" "SPI4_MISO" "SPI4_MOSI" "USART1_CTS" "USART5_DE" "SPI3_MISO" "SPI3_MOSI" "USART2_CTS" "I2S5_WS" "LPUART1_RTS" "USART4_DE" "SPI2_MISO" "SPI2_MOSI" "USART3_CTS" "I2S4_WS" "USART3_DE" "USART6_TX" "SPI1_MISO" "SPI1_MOSI" "USART4_CTS" "RCC_OSC_IN" "I2S3_WS" "USART2_DE" "USART5_TX" "RCC_OSC32_IN" "I2S2_WS" "CAN_TX" "SPI1_NSS" "USART1_DE" "USART4_TX" "USART6_RX" "SYS_SWDIO" "I2C1_SCL" "I2S1_WS" "SPI2_NSS" "USART3_TX" "USART5_RX" "SYS_JTCK-SWCLK" "I2C2_SCL" "CAN_RX" "SPI3_NSS" "USART2_TX" "USART4_RX" "I2C3_SCL" "SPI4_NSS" "USART1_TX" "USART3_RX" "USART5_CK" "SPI5_NSS" "USART2_RX" "USART1_RX" "USART3_CK" "USART2_CK" "SYS_JTMS-SWDIO" "USART1_CK" "I2S5_CK" "I2S1_MCK" "I2S4_CK" "I2S2_MCK" "USART1_RTS" "I2S3_CK" "I2S3_MCK" "USART2_RTS" "I2S2_CK"]
Supported parameters
The supported special query parameters are:
- _exist : List of attributes that the returned documents must contain.
- _sort : The returned documents will be sort according to the list of attributes specified
by this parameter, applied in the order they appear. Sorting in descending fashion can
be obtained by starting a sorting attribute by a minus sign "−". This can take 2 additional value besides
the list of existing properties:
area
is an alias fordimensions.area
andcost
is an alias forprice
. - _distinct : If this parameter exist, it must be an attribute string, the query will return the set of available values for the specified attribute in the list of documents that meet all the query constraints.
Regular filter properties:
- We can filter on a part attribute :
{"category": "resistor"}
. - We can filter on a capacitance, resistance and inductance, those constraints are replaced by interval allowing for queries with a precision of 0.0005Ω, 0.01A and 0.01F respectively.
- We can filter on a tolerance :
{"tolerance": 0.05}
will constrain the tolerance to be an exact match{"tolerance": {"min": -0.05, "max": 0.05}}
. Returns500 Bad Request
if this is not a positive double - We can filter on a nested parameter using
.
:{"dimensions.x": 11}
- We can filter on a list of values giving a list :
{"case": ["Axial", "0402"]}
- We can filter with an inequality prepending
min-
ormax-
:{"max−rated-power": 0.05}
. max-x
,max-y
andmax-z
are shorthands formax-dimensions.x
,max-dimensions.y
,max-dimensions.z
- We interpret
minimum_quantity
as a floor on bulk purchase size; we automatically convertminimum_quantity
tomax-minimum_quantity
.
Typo checker
The typo checker is triggered when an unsupported parameter is used in a query where the "category" parameter is present and amongst ["resistor", "capacitor", "inductor", "microcontroller"]. When the typo checker detects an unsupported parameter, it will throw an error printing the list of supported parameters of the category. Example:
$ jitx repl
stanza> import jitx/commands
stanza> println $ dbquery-all(["category" => "capacitor" "typo" => 1.0e-06])
The invalid attributes ("typo") are used in the query ["category" => "capacitor" "typo" => 1.0e-06].
Valid attributes for category capacitor must be amongst:
- anode
- capacitance
- case
- category
- dimensions.area
- dimensions.x
- dimensions.y
- dimensions.z
- electrolyte
- esr
- esr_frequency
- manufacturer
- minimum_quantity
- mounting
- mpn
- price
- rated-current-pk
- rated-current-rms
- rated-temperature.max
- rated-temperature.min
- rated-voltage
- rated-voltage-ac
- stock
- temperature-coefficient.change
- temperature-coefficient.code
- temperature-coefficient.lower-temperature
- temperature-coefficient.raw_data
- temperature-coefficient.tolerance
- temperature-coefficient.upper-temperature
- temperature-coefficient.value
- tolerance
- tolerance.max
- tolerance.min
- trust
- type
- update_date
Sourcing data
If the request contains the attributes _stock
or/and _sellers
:
more advanced part sourcing data will be independently queried and integrated to the results, disregarding sellers that:
- are not in the list
- do not have an inventory level above the queried stock
- do not have a pricing offering with a minimum quantity lower that the queried stock In this case, any part that does not have sourcing data that meets the criteria above will be ignored.
If _stock
is 0
or _sellers
is []
, then no filtering out is done using those properties. In particular querying with
both [_"stock" => 0, "_sellers" => []]
will append the whole part sourcing data to the result for any sellers at any inventory
stock and with any price offerings.
We recommend to always provide a list of allowed sellers as the part sourcing data contains unverified sellers that may be intermediaries.
A sellers
key will be added to the returned json with the sourcing data meeting the request.
The best price from each seller is aggregated in the resolved_price
key in each seller data.
The best price from all qualifying offerings is returned in the resolved_price
key at the root of the json.
"sellers": [
{
"company_name": ...,
"resolved_price": ...,
"offers": [{
"inventory_level": ...,
"prices": [{"quantity": ..., "converted_price": ...}]
},...]
}
]
"resolved_price": ...
Sorting by price
cost
and price
can be used used interchangeably. We are using cost
in what follows.
If _sort
is ["cost"]
(respectively ["-cost"]
), results will be sorted by increasing (respectively decreasing) price.
If neither _stock
nor _sellers
are parameters of the query, then the price used to sort on for resistors, capacitors and resistors
is the price for 1 unit from Digikey (updated every week). For the microcontroller
category, no sorting on price will occur, please see
the next section.
If _stock
or _sellers
are parameters of the query, then real-time sourcing data is integrating in the query (see section "Sourcing data" above).
At most 1000 parts meeting the query are retrieved from the JITX database, then sourcing data for each part is retrieved,
the price used to sort on is the resolved_price
attribute.
Utilities
This section contains info on JITX utility functions.
Stateful
Stateful
is a useful tool for managing property states.
Data Types
public defstruct Stateful<K,V> :
current: K|False
states: Tuple<KeyValue<K,V>>
public defn Stateful<?K,?V> (states:Seqable<KeyValue<?K, ?V>>) -> Stateful
public defn Stateful<?K,?V> (current:JITXValue, states:Seqable<KeyValue<?K, ?V>>) -> Stateful
Stateful
is a collection of states with a possibly current state.
current
: the current state, or false if there is no current state.states
: mappings from a state's name to a value.
Function
public defn get<?K,?V> (s:Stateful<?K,?V>, state:?K) -> V :
Returns the value for the given state.
public defn state-names<?K,?V> (s:Stateful<?K,?V>) -> Tuple<K> :
Returns the names of all states.
Additional Syntaxes
state(<object>.<property>) = <state>
Set the property's state to state
. It is required that object.property
is a Stateful. This statement must be declared inside a pcb-module, pcb-component, or support option.
state(<object>.<property>)
Get the property's current state. It is required that both object.property
is a Stateful and there is a current state.
state?(<object>.<property>)
Get the property's current state. It is required that object.property
is a Stateful. Returns false if there is no current state.
has-state?(<object>.<property>, <state>)
Returns whether state
is one of object.property
's states. It is required that object.property
is a Stateful.
pcb-module main :
port p : pin[5]
;Assign a Stateful property to pin p[3] in a USB-powered system.
;The low state load-current is 100mA and the high state load-current is 1.5A.
;No initial state is assigned.
property(p[3].load-current) = Stateful(["low" => 100.0e-3,
"high" => 1.5])
;Get a list of state names. Returns`["low" "high"]`
state-names(property(p[3].load-current))
;Try getting the current state. Returns false.
state?(p[3].load-current)
;Set the state to "low".
;Much like properties, states can be set inside a module, component, or support option.
state(p[3].load-current) = "low"
;Get the current state. Returns "low".
state(p[3].load-current)
;Does the property have a "high" state? Returns true.
has-state?(p[3].load-current, "high")
The above pcb-module demonstrates how all syntaxes can be used.
Toleranced
Toleranced
is a useful tool for managing values with some tolerance.
Data Types
public defstruct Toleranced :
typ: Double
tol+: Double
tol-: Double
Toleranced
is a generic value with upper and lower bound tolerances that may be off-center (upper tolerance != lower tolerance). It represents the inequality (typ - tol-) <= T <= (typ + tol+)
.
typ
: the typical valuetol+
: the upper tolerancetol-
: the lower tolerance
Functions
public defn min-typ-max (min:Double, typ:Double, max:Double) -> Toleranced
Creates a Toleranced
value from minimum/typical/maximum values.
public defn min-max (min:Double, max:Double) -> Toleranced
Creates a Toleranced
value from minimum/maximum values with typ as the midpoint.
public defn tol% (typ:Double, tol+:Double, tol-:Double) -> Toleranced
Creates a Toleranced
value using percentages (0.0 <= tol+/tol- <= 100.0) for +/- tolerance.
public defn tol% (typ:Double, tol:Double) -> Toleranced
Creates a symmetric Toleranced
value using percentages (0.0 <= tol <= 100.0) for +/- tolerance.
public defn tol (typ:Double, tol+:Double, tol-:Double) -> Toleranced
Creates a Toleranced
value using relative values for +/- tolerance.
public defn tol (typ:Double, tol:Double) -> Toleranced
Creates a Toleranced
value using the symmetric range for +/- tolerance.
public defn tol+% (t:Toleranced) -> Double
Returns the upper tolerance, as a percentage.
public defn tol-% (t:Toleranced) -> Double
Returns the lower tolerance, as a percentage.
public defn max-value (t:Toleranced) -> Double
Returns the maximum value of the tolerance.
public defn typ-value (t:Toleranced) -> Double
Returns the typ value of the tolerance (not necessarily the center value).
public defn min-value (t:Toleranced) -> Double
Returns the minimum value of the tolerance.
public defn center-value (t:Toleranced) -> Double
Returns the center value of the tolerance, which may not be the typical value.
public defn in-range? (t:Toleranced, value:Double) -> True|False
Checks if a value is within the tolerance's range.
public defn in-range? (t:Toleranced, value:Toleranced) -> True|False
Checks if a value Toleranced min/max is within the first argument tolerance's min/max.
public defn tolerance-range (t:Toleranced) -> Double
Returns the difference between maximum and minimum values of the tolerance.
public defn minus (a:Toleranced, b:Toleranced) -> Toleranced
public defn minus (a:Double, b:Toleranced) -> Toleranced
Subtraction of Toleranced values. Arithmetic with Toleranced values are useful for the worst case analysis, but be aware the bounds can grow quickly so it's important to use realistic ranges.
public defn plus (a:Toleranced, b:Toleranced) -> Toleranced
public defn plus (a:Double, b:Toleranced) -> Toleranced
Addition of Toleranced values. See note under toleranced subtraction.
public defn divide (a:Toleranced, b:Toleranced) -> Toleranced
public defn divide (a:Double, b:Toleranced) -> Toleranced
Division of Toleranced values. See note under toleranced subtraction.
public defn times (a:Toleranced, b:Toleranced) -> Toleranced
public defn times (a:Double, b:Toleranced) -> Toleranced
Multiplication of Toleranced values. See note under toleranced subtraction.
Example usage
; Create a range between -0.3 and 5.0
val range-1 = min-max(-0.3, 5.0)
; Create a range within 20% of 500.0
val range-2 = tol%(500.0, 20.0)
; Check if range-2 includes range-1
in-range?(range-2, range-1)
; Get the minimum value of range-1
min-value(range-1)
ocdb/utils/design-vars
Design variables are configuration settings for OCDB applied to the current design (or sets of designs, in multi-board projects). These variables control generator mechanisms across OCDB.
Data Types
public pcb-enum ocdb/utils/design-vars/DensityLevel:
DensityLevelA
DensityLevelB
DensityLevelC
DensityLevel
is a helper enum that represents the desired density level of the current design (A, B, or C, with "A" being the least dense and "C" being the most dense). It is used to generate IPC compliant land patterns.
Global Variables
public var OPERATING-TEMPERATURE = min-max(0.0, 25.0)
The operating temperature range of the design (C).
public var OPTIMIZE-FOR = ["area"]
The desired optimization approach by JITX internal generators. Supported values are "area" and "cost".
public var MAX-Z = 500.0
The maximum height of a component (mm).
public var MIN-PKG = "0201"
The minimum package size of a passive component. Used to find components that can be placed by the fabricator when pulling components from the JITX database.
public var APPROVED-DISTRIBUTOR-LIST = ["Allied Electronics & Automation"
"Arrow Electronics"
"Avnet"
"Digi-Key"
"Future Electronics"
"Mouser"
"Newark"]
The list of approved distributors. Set it to false to allow any distributor.
public var DESIGN-QUANTITY = 100
The number of boards you intend to fabricate. Used to find components in stock when pulling components from the JITX database.
public var ALLOW-NON-STOCK = true
Whether to allow components to be out of stock. JITX gives warning messages when components are out of stock and ALLOW-NON-STOCK
is true
(the default behavior). If ALLOW-NON-STOCK
is false
, JITX throws an exception when any component is out of stock.
public var PREFERRED-MOUNTING = "smd"
The preferred mounting style of a component pulled from the JITX database. Supported values are ["smd", "through-hole"].
public var MIN-CAP-VOLTAGE = 10.0
Minimum voltage to allow for capacitors
public var DEFAULT-ALLOW-ALL-VENDOR-PART-NUMBERS = false
Use all parts from the Parts Database for existing parametric queries (e.g. chip-resistor
). If false, restrict these queries to a set of parts with a higher confidence in quality.
public var DENSITY-LEVEL: DensityLevel = DensityLevelC
The preferred density level of the design. Used when generating land patterns.
public var MIN-SOLDER-MASK-SLIVER: Double = 0.15
The minimum space allowed between non overlapping regions of solder mask opening. Used when checking land patterns.
public var MIN-OUTER-LAYER-PAD-SIZE: Double = 0.2032
The minimum size of a circular pad on the outer layers. Used when computing through hole land patterns.
public var MAX-HOLE-SIZE-TOLERANCE: Double = 0.0508
The board fabricator's tolerance on the diameter of the largest hole (mm). Used when computing through hole land patterns.
public var MIN-HOLE-SIZE-TOLERANCE: Double = 0.0508
The board fabricator's tolerance on the diameter of the smallest hole (mm). Used when computing through hole land patterns.
public var HOLE-POSITION-TOLERANCE: Double = 0.0508
The board fabricator's tolerance on placing the center of any hole (mm). Used when computing through hole land patterns.
ocdb/utils/landpatterns
Land patterns (sometimes called footprints) are the geometry associated with a single package that is to be placed on a design. In JITX, they are represented using the pcb-landpattern
statement. The package ocdb/utils/landpatterns
contains utilities for generating optimized land-patterns from parameters.
Table of Contents
- Helpful References
- Definitions
- Two Pin Land Patterns
- Small Outline (SO) Land Patterns
- QFP Land Patterns
- QFN Land Patterns
- BGA Land Patterns
Helpful References
- JEDEC JESD30E, "Descriptive Designation System for Semiconductor-device packages"
- IPC 7351B, Generic Requirements for Surface Mount Design and Land Pattern Standard
Definitions
- Package: the physical device that will be placed (including the exterior plastic packaging and metal leads or terminations of a component). A package has one or more leads or terminations..
- Lead or Termination: The conductors on the package that interface with the electrical device.
- Land Pattern: the geometry on the layers of the board that sits underneath the package. A landpattern has one or more pads or and zero or more layers.
- Pad: the geometry on the layers associated with a single lead or termination of the component.
Two Pin Land Patterns
Chip Land Patterns
Two pin chip land patterns are commonly used for passive devices. The following generators are used to generate land patterns for these components that are compliant with IPC specifications.
public defn make-two-pin-chip-landpattern (length:Toleranced,
width:Toleranced,
lead-length:Toleranced)
Create a two pin surface mount (chip) non-polarized land pattern.
length
: the length of the component.width
: the width of the component.lead-length
: the length of the leads of the component.
public defn make-two-pin-chip-landpattern (length:Toleranced,
width:Toleranced,
lead-length:Toleranced,
polarized?:True|False)
Create a two pin surface mount (chip) land pattern with optional polarization
length
: the length of the component.width
: the width of the component.lead-length
: the length of the leads of the component.polarized?
:true
if the land pattern is polarized,false
otherwise.
public defn make-two-pin-chip-landpattern (length:Toleranced,
width:Toleranced,
lead-length:Toleranced,
lead-width:Toleranced)
Create a two pin surface mount (chip) land pattern with a lead width that is different from the component width.
length
: the length of the component.width
: the width of the component.lead-length
: the length of the leads of the component.lead-width
: the width of the leads of the component, which may be different than the width of the component.
public defn make-two-pin-chip-landpattern (length:Toleranced,
width:Toleranced,
lead-length:Toleranced,
lead-width:Toleranced,
polarized?:True|False)
Create a two pin surface mount (chip) land pattern with a lead width that is different from the component width and optional polarization.
length
: the length of the component.width
: the width of the component.lead-length
: the length of the leads of the component.lead-width
: the width of the leads of the component, which may be different than the width of the component.polarized?
:true
if the land pattern is polarized,false
otherwise.
Example : A Surface Mount Capacitor
The associated datasheet can be found here
pcb-landpattern TH3Axxx :
make-two-pin-chip-landpattern(tol(3.2, 0.2), ; package-length, L
tol(1.6, 0.2), ; package-width, W
tol(0.8, 0.3), ; lead-length, P
tol(1.2, 0.1), ; lead-width, Tw
true) ; polarized ?
Through Hole Land Patterns
Radial Through Hole Land Patterns
Supported two-pin through hole land patterns are either axial or radial.
public defn make-two-pin-radial-landpattern (component-diameter:Toleranced,
lead-spacing:Double,
lead-diameter:Toleranced)
Create a two-pin radial landpattern.
component-diameter
: the diameter of the component packagelead-spacing
: the distance between leads.lead-diameter
: the diameter of the leads.
public defn make-two-pin-radial-landpattern (component-diameter:Toleranced,
lead-spacing:Double,
lead-diameter:Toleranced
polarized?:True|False)
Create a two-pin radial landpattern with optional polarity.
component-diameter
: the diameter of the component packagelead-spacing
: the distance between leads.lead-diameter
: the diameter of the leads.polarized?
:true
if the land pattern is polarized,false
otherwise.
Axial Through Hole Land Patterns
public defn make-two-pin-axial-landpattern (component-diameter:Toleranced,
lead-spacing:Double,
lead-diameter:Toleranced)
Create a two-pin radial landpattern.
component-diameter
: the diameter of the component packagelead-spacing
: the distance between leads.lead-diameter
: the diameter of the leads.
public defn make-two-pin-axial-landpattern (component-diameter:Toleranced,
lead-spacing:Double,
lead-diameter:Toleranced
polarized?:True|False)
Create a two-pin radial landpattern with optional polarity.
component-diameter
: the diameter of the component packagelead-spacing
: the distance between leads.lead-diameter
: the diameter of the leads.polarized?
:true
if the land pattern is polarized,false
otherwise.
Small Outline Land Patterns
A small outline (SO) package is a package with pins along two sides of the component.
JEDEC symbol to JITX Generator Arguments
The following table may help in converting JEDEC-style dimensions into arguments for the JITX small outline landpattern generator.
JEDEC Symbol | JITX Argument |
---|---|
b | terminal-width |
D | package-length |
E | lead-span |
E1 | package-width |
e | pitch |
L | terminal-length |
n | num-pins |
public defn make-n-pin-soic-landpattern (num-pins:Int,
pitch:Double, ; e
lead-span:Toleranced, ; E
package-length:Toleranced, ; E1
package-width:Toleranced, ; D
terminal-length:Toleranced, ; L
terminal-width:Toleranced) ; b
Create an SO land pattern.
num-pins
: the number of pins of the package (must be even).pitch
: the pitch of the leads or terminals (mm),e
in the table above.lead-span
: the distance from lead-edge to lead-edge (mm),E
in the table above.package-length
: the size of the package in the same dimension as thelead-span
(mm),E1
in the table above.package-width
: the size of the package in the orthogonal dimension aslead-span
(mm),D
in the table above.terminal-length
: the length of terminals or leads in contact with the land pattern (mm),L
in the table above.terminal-width
: the size of the terminals or leads in contact with the land pattern, in the orthogonal dimension toterminal-length
(mm),b
in the table above.
Example Usage
pcb-landpattern SO8N :
make-n-pin-soic-landpattern(8, ; number of pins
1.27 ; pitch
min-typ-max(5.8, 6.0, 6.2) ; lead-span
min-typ-max(4.8, 4.9, 5.0) ; package-length
min-typ-max(3.8, 3.9, 4.0) ; package-width
min-typ-max(0.4, 0.8, 1.27) ; lead-length, using average as typical
min-typ-max(0.28, 0.38, 0.48)) ; lead-width, using average as typical
See The Model a Land Pattern tutorial for more details on parametric land pattern generation.
SOT Land Patterns
SOT landpatterns can be defined using the more general purpose API call, make-dual-row-smd-landpattern
which creates a two-row SMD land pattern with different numbers of pins on either side.
public defn make-dual-row-smd-landpattern (primary-num-pins: Int,
secondary-num-pins: Int,
primary-pin-pitch: Double,
secondary-pin-pitch: Double,
lead-span: Toleranced,
package-length: Toleranced,
package-width: Toleranced,
terminal-length: Toleranced,
terminal-width: Toleranced,
polarity-marker?: True|False)
primary-num-pins
: the number of pins on one side of the land patternsecondary-num-pins
: the number of pins on the other side of the land patternprimary-pin-pitch
: the pitch of pins on the primary side of the land patternsecondary-pin-pitch
: the pitch of the pins on the other side of the land patternlead-span
: the lead span across the land patternpackage-length
: the length of the packagepackage-width
: the width of the packageterminal-length
: the length of the terminalsterminal-width
: the width of the terminalspolarity-marker?
: whether to add a polarity marker to the landpattern or not
Example Usage
pcb-landpattern SOT-23 :
make-dual-row-smd-landpattern(
n1, n2, p1, p2,
lead-span, package-length, package-width,
terminal-length, terminal-width,
polarity-marker?: True|False) where :
val n1 = 2
val n2 = 1
val p1 = 1.9
val p2 = 0.0 ; unneeded for one pin
val lead-span = min-typ-max(2.1, 2.37, 2.634) ; E
val package-length = tol(1.3, 0.1) ; E1
val package-width = min-typ-max(2.8, 2.9, 3.04), ; D
val terminal-length = tol(0.4, 0.1)
val terminal-width = tol(0.5, 0.1)
val polarity-marker? = false
QFP Land Patterns
A quad flat package (QFP) is a package with pins along all four sides of the component.
JEDEC symbol to JITX Generator Arguments
JEDEC Symbol | JITX Argument |
---|---|
b | terminal-width |
D, E | lead-span |
E1, D1 | package-size |
e | pitch |
L | terminal-length |
n | num-pins |
public defn make-qfp-landpattern (num-pins:Int,
pitch:Double, ; e
lead-span:Toleranced, ; E, D
package-size:Toleranced, ; E1, D1
terminal-length:Toleranced, ; L
terminal-width:Toleranced ; b
exposed-metal-heat-feature?:Shape ; optional thermal pad
num-pins
: the number of pins of the package (must be even).pitch
: the pitch of the leads or terminals (mm),e
in the table above.lead-span
: the distance from lead-edge to lead-edge (mm),E
in the table above.package-size
: the size of the package,E1, D1
in the table above. The package is assumed to be square.terminal-length
: the length of terminals or leads in contact with the land pattern (mm),L
in the table above.terminal-width
: the size of the terminals or leads in contact with the land pattern, in the orthogonal dimension toterminal-length
(mm),b
in the table above.exposed-metal-heat-feature?
: an optional parameter for a thermal pad shape.
Example Usage
pcb-landpattern QFP-100 :
make-qfp-landpattern(
num-pins, pitch,
lead-span, package-size,
terminal-length, terminal-width,
thermal-pad) where :
val num-pins = 100 ; the total number of pins
val pitch = 0.5 ; the distance between pin centers
val lead-span = min-typ-max(15.8, 16.0, 16.2) ; the distance between the outer edges of the leads
val package-size = min-typ-max(13.8, 14.0, 14.2) ; the size of the package, assumed to be square
val terminal-width = min-typ-max(0.17, 0.22, 0.27) ; the width of the leads
val terminal-length = min-typ-max(0.45, 0.60, 0.70) ; the length of the leads
val thermal-pad = false
QFN Land Patterns
A quad flat package no-lead (QFN) is a quad landpattern where the terminals are pads on the underside of the component, as opposed to leads that extend outside the component (see QFP Land Patterns).
JEDEC symbol to JITX Generator Arguments
JEDEC Symbol | JITX Argument |
---|---|
b | terminal-width |
E, D | package-size |
e | pitch |
L | terminal-length |
n | num-pins |
public defn make-qfn-landpattern (num-pins:Int,
pitch:Double, ; e
package-size:Toleranced, ; E, D
terminal-length:Toleranced, ; L
terminal-width:Toleranced, ; b
corner-pads?:False|[Pad, Pad], ; L1 x b
exposed-metal-heat-feature?:Shape|False) : ; optional thermal pad
num-pins
: the number of pins of the package (must be even).pitch
: the pitch of the leads or terminals (mm),e
in the table above.package-size
: the size of the package,E
,D
in the table above. The package is assumed to be square.terminal-length
: the length of terminals or leads in contact with the land pattern (mm),L
in the table above.terminal-width
: the size of the terminals or leads in contact with the land pattern, in the orthogonal dimension toterminal-length
(mm),b
in the table above.corner-pads?
: optional pair ofpcb-pad
definitions to use to replace the corner pads on the landpattern.exposed-metal-heat-feature?
, an optional
Example Usage
pcb-landpattern QFN-28 :
make-qfn-landpattern(
num-pins, pitch,
package-size, lead-length, lead-width
corner-pads,
thermal-pad,) where :
val num-pins = 28 ; the total number of pins, must be divisible by 4
val pitch = 0.5 ; the pitch of the pins
val package-size = min-typ-max(3.90, 4.00, 4.10) ; the size of the package, assumed to be square
val lead-length = min-typ-max(0.30, 0.40, 0.50) ; the length of the lead terminals
val lead-width = min-typ-max(0.20, 0.25, 0.30) ; the width of the lead terminals
; Optional Corner Pad Modifiers
val corner-pads = [
smd-pad(loc(-0.1, 0.0) * Rectangle(0.45, 0.21))
smd-pad(loc(-0.1, 0.0) * Rectangle(0.45, 0.21))
]
; Optional thermal pad
val thermal-pad = false
Note: some trial and error might be required to find dimensions for corner pad overrides. In this case, the length was found using the
max
value of L1 in the table, and the width was found empirically by measuring the width of the other pads.
BGA Land Patterns
A ball grid array (BGA) package uses a matrix of solder balls on the bottom of the package, as opposed to exposed metal terminals.
JEDEC symbol to JITX Generator Arguments
JEDEC Symbol | JITX Argument |
---|---|
num-rows | |
num-cols | |
b | ball-diameter |
D | package-width |
E | package-length |
e | pitch |
num-cols:Int,
pitch:Double,
ball-diameter:Double,
package-length:Toleranced,
package-width:Toleranced,
modifier:BGAModifier) :
num-rows
: the number of rows of packagenum-cols
: the number of columns of the packagepitch
: the pitch of the solder bumpsball-diameter
: the nominal/typical diameter of the solder ballspackage-length
: the length of the packagepackage-width
: the width of the packagemodifier
: the depopulation and/or pitch modifier
Example Usage
pcb-landpattern BGA-64 :
make-bga-landpattern(
num-rows, num-cols
pitch, ball-diameter
package-length, package-width
modifier
) where :
val num-rows = 8 ; Number of rows in the matrix
val num-cols = 8 ; Number of columns in the matrix
val pitch = 0.50 ; the spacing between ball centers
val ball-diameter = 0.28 ; the diameter of the solder balls
val package-length = min-typ-max(4.85, 5.0, 5.15) ; the length of the package
val package-width = min-typ-max(4.85, 5.0, 5.15) ; the width of the package
; The BGA Modifier. See ocdb/utils/landpatterns for more
; modifiers, supported are StaggeredMatrix, EquilateralTriangleMatrix,
; PerimeterMatrix, ThermallyEnhancedMatrix.
val modifier = FullMatrix()
Depopulation Modifiers
The make-bga-package
argument uses a special argument named modifier
which allows for a variety of depopulation and ball spacing strategies. The currently supported depopulation modifiers are:
FullMatrix
In a FullMatrix
(the default) no pads will be depopulated.
defn FullMatrix () -> BGAModifier
StaggeredMatrix
In a StaggeredMatrix
, every other pad is depopulated.
defn StaggeredMatrix () -> BGAModifier
PerimeterMatrix
In a PerimeterMatrix
, a range of rows and columns in the center will be depopulated
defn PerimeterMatrix (rows:Range, cols:Range) -> BGAModifier
rows
: the range of rows to depopulatecols
: the range of cols to depopulate
Usage :
; rows 3,4,6 and columns 4,5 will be depopulated.
val modifier = PerimeterMatrix(3 through 6, 4 through 5)
ThermallyEnhancedMatrix
A ThermallyEnhancedMatrix
is like a PerimeterMatrix, except with some region in the center remaining populated.
defn ThermallyEnhancedMatrix (perimeter-rows:Range, perimeter-cols:Range
interior-rows:Range, interior-cols:Range ) -> BGAModifier
perimeter-rows
: the range of rows to depopulateperimeter-cols
: the range of columns to depopulateinterior-rows
: the range of rows in the center to remain populatedinterior-cols
: the range of columns in the center to remain populated
Usage :
; Pins (5,5) through (6,6) will be populated, while the other
; pins within the range (4,4) through (7,7) will be depopulated.
val modifier = ThermallyEnhancedMatrix(
4 through 7, 4 through 7,
5 through 6, 5 through 6,
))
EquilateralTriangleMatrix
An "EquilateralTriangleMatrix" is like a staggered matrix, but where the pads are all equidistant from each other. This means for any three adjacent pads with two in the same row or column, the centers of the pads form an equilateral triangle.
defn EquilateralTriangleMatrix () -> BGAModifier
CustomDepop
The CustomDepop
modifier allows the caller to specify which pins are depopulated using a callback.
defn CustomDepop (callback: (Int, Int) -> True|False)
Usage :
defn depop? (row:Int, col:Int) :
; Specify a list of custom depopulated pins
val depopulated = [
[0, 2]
[0, 5]
[2, 8]
[3, 0]
[5, 8]
[6, 0]
[8, 3]
[8, 6]
]
contains?(depopulated, [row, col])
val modifier = CustomDepop(depop?)
ocdb/utils/land-protrusions
ocdb/utils/land-protrusions
is a set of lookup tables used to satisfy IPC compliance during the generation of land patterns. It defines a set of LandProtrusionType
s that represent metadata about package leads or terminations that is used alongside DensityLevel
in order to compute offsets to the pads and courtyards of land patterns.
Data Types
public pcb-enum ocdb/utils/land-protrusions/LandProtrusionType :
SmallFlatRibbonLLeads, ; Flat ribbon L leads for pitch less than 0.0625mm
SmallGullWingLeads, ; Gull wing leads with pitch less than 0.0625mm
BigFlatRibbonLLeads, ; Flat ribbon L leads for pitch greater than 0.0625mm
BigGullWingLeads, ; GullWing leads for pitch greater than 0.0625mm
JLeads, ; J Leads
SmallRectangularLeads, ; Leads for rectangular components (chip capacitors, resistors, inductors) smaller than 0601
BigRectangularLeads, ; Leads for rectangular components (chip capacitors, resistors, inductors) 0601 or larger
CylindricalLeads, ; Cylindrical end cap leads
LeadlessChipCarrierLeads, ; Lead less chip carrier leads
ConcaveChipArrayLeads, ; Concave chip array leads
ConvexChipArrayLeads, ; Convex chip array leads
FlatChipArrayLeads, ; Flat chip array leads
ButtJointLeads, ; Butt joint leads
InwardFlatRibbonLLeads, ; Inward flat ribbons L leads
FlatLugLeads, ; flat lug leads
QuadFlatNoLeads, ; quad flat components without leads
SmallOutlineNoLeads, ; small outline packages without leads
SmallOutlineFlatLeads, ; small outline flat leads
ShortTwoPinCrystalLeads, ; two pin crystal components shorter than 10mm
ShortAluminumElectrolyticLeads, ; electrolytic capacitor shorter than 10mm
TallTwoPinCrystalLeads, ; two pin crystal components 10mm or taller
TallAluminumElectrolyticLeads, ; electrolytic capacitor 10mm or taller
LandProtrusionType
is a helper enum for various kinds of leads and terminations on components. It is used to configure their generated land patterns.
public pcb-struct ocdb/utils/land-protrusions/LeadFillets:
toe:Double ; space away from the component towards the board
heel:Double ; space towards the component
side:Double ; space on the edges of the lead
courtyard-excess:Double ; additional area to add to the courtyard
LeadFillets
is a helper struct that represents the space added to pads and courtyard area when generating land patterns.
Functions
public defn lead-fillets (lead-type:LandProtrusionType) -> LeadFillets
public defn lead-fillets (lead-type:LandProtrusionType, density-level:DensityLevel) -> LeadFillets
A helper function to lookup the LeadFillets
corresponding to a LandProtrusionType
given a DensityLevel
. If not supplied, the DensityLevel
used is the default defined in Design Variables
public defn bga-courtyard-excess (density-level:DensityLevel) -> Double
A helper function to lookup the courtyard excess when computing BGA land patterns.
Properties
JITX allows us to associate arbitrary properties with pins, components, and modules. These properties are used throughout OCDB for design calculations and design review checks. Everyone is free to define and use their own parameters, but when we collaborate on a shared set it is easier to write better automation. What follows is documentation of the properties defined in OCDB.
Component properties
These are properties associated with components, including passive components like resistors, capacitors, and inductors.
Generic properties
These properties apply to all components.
Name | Type | Values | Unit | Description |
---|---|---|---|---|
temperature | Double | degC | Bulk temperature of the component | |
rated-temperature | Toleranced False | e.g. min-max() or false | degC | Rated operating temperature of component. Use false if property does not apply. |
trust | String | “low” | The data for this component came from a distributor/other | |
“datasheet”, "medium" | The data from this component came from the datasheet | |||
“built”, "high" | This component was used on a board without issue | |||
“characterized” | The this model data was characterized in the lab | |||
dimensions | [Double, Double, Double] | [Length, Width, Height] | [mm, mm, mm] | Length, width and height (x,y,z) of the bounding box of the component |
mounting | String | “smd” “through-hole” | How is this component mounted to the board | |
case | String | “0402”, "0805" | Common name of the component package |
Resistor properties
These are properties associated with resistors, as used by gen-res-cmp
in ocdb/utils/generic-components
.
Name | Type | Values | Unit | Description |
---|---|---|---|---|
resistor | True|False | true | Identifies resistor component | |
type | String | “thick-film”, “metal-film”, ... | Type of resistor | |
resistance | Double | Ohm | Nominal resistance | |
tolerance | Double | Ohms/Ohm | Guaranteed tolerance from manufacturer | |
rated-voltage | Double | Volts | Maximum voltage rating from manufacturer | |
rated-power | Double|PWL[temp] | Watts|[degC, Watts] | Power dissipation limit as rated by manufacturer. One value, or set of points for PWL function. | |
derating | PWL[temp] | [degC, derate factor] | Temperature-based derating of resistance | |
TCR | Double | Ohms/Ohm*degC | Temperature coefficient of resistance |
Capacitor properties
These are properties associated with capacitors, as used by gen-cap-cmp
in ocdb/utils/generic-components
.
Name | Type | Values | Unit | Description |
---|---|---|---|---|
capacitor | True|False | true | Identifies capacitor component | |
type | String | “ceramic”, “film”, “electrolytic" | Type of capacitor | |
anode | String | “aluminum”, “tantalum”, “niobium-oxide” | Anode material of electrolytic capacitor | |
electrolyte | String | “polymer”, “manganese-dioxide”, “hybrid”, “non-solid” | Electrolyte material of electrolytic capacitor | |
temperature-coefficient | String | “X7R”,"X5R", "NP0", "C0G",... | Temperature coefficient of capacitance | |
capacitance | Double | Farad | Nominal capacitance | |
tolerance | Double | Farad/Farad | Guaranteed tolerance from manufacturer | |
rated-voltage | Double | Volts | Maximum voltage rating from manufacturer | |
rated-current-pk | Double | Amperes | Maximum peak current rating from manufacturer | |
rated-current-rms | Double|PWL[temp] | Amperes | Maximum rms current rating from manufacturer |
Inductor properties
These are properties associated with inductors, as used by gen-ind-cmp
in ocdb/utils/generic-components
.
Name | Type | Values | Unit | Description |
---|---|---|---|---|
inductor | True|False | true | Identifies component as inductor | |
type | String | “wirewound”, “Thick Film”, ... | Type of inductor | |
material-core | String | “ceramic”, “ferrite”, ... | Composition of inductor | |
inductance | Double | Henry | Nominal inductance | |
tolerance | Double | henry/henry | Guaranteed tolerance from manufacturer | |
current-rating | Double | Amperes | Maximum steady-state current rating from manufacturer | |
saturation-current | Double | Amperes | Percentage inductance drop (typ 20-30%) at peak currents | |
shielding | String | “semi-shielded”, “shielded”, “unshielded” | Magnetic field status | |
dc-resistance | Double | ohm | Nominal resistance | |
quality-factor | Double | ratio@freq | Loss factor inverse - ratio between inductors resistance and inductance | |
self-resonant-frequency | Double | Hertz | Frequency at which inductor impedance becomes very high / open circuit |
Crystal properties
These are properties associated with crystals, as used by gen-xtal-cmp
in ocdb/utils/generic-components
.
Name | Type | Values | Unit | Description |
---|---|---|---|---|
crystal-resonator | CrystalResonator() | see below | Collection of properties for a resonator |
public pcb-struct ocdb/utils/property-structs/CrystalResonator :
load-capacitance:Double ; Load capacitance in Farads
shunt-capacitance:Double ; Shunt capacitance in Farads
motional-capacitance:Double ; Motional capacitance in Farads
ESR:Double ; Series resistance of crystal in Ohms
frequency:Double ; Fundamental frequency in Hz
frequency-tolerance:Double ; Frequency tolerance in Hz
max-drive-level:Double ; Max drive level in W
; Example call
property(self.crystal-resonator) = ocdb/utils/property-structs/CrystalResonator(cl, cs, cm, esr, f, df * f, dl)
Pin properties
These properties are associated with component pins like power, reset, and logic pins.
Power pins
Name | Type | Values | Unit | Description |
---|---|---|---|---|
power-pin | PowerPin(Toleranced) | e.g. min-max() or min-typ-max() | Volts | Power pin definition using the structure PowerPin() taking a Toleranced argument for the allowable voltage range |
supply-pin | PowerSupplyPin(Toleranced, Double) | Volts|[Volts,Amps] | Nominal voltage and optional current capacity of the power pin | |
gnd-ref | pin or net object | JITXObject | Identifier for a pin or net that is the 0v reference for this pin | |
power-request | [Double,Double,Double] | [Voltage, Current, Noise] | [Volts, Amperes, Volts] | Defines a load by nominal voltage, current, and allowable noise to be satisfied by the power solver. |
Logic pins
Digital pins
These are properties to check the generic rated voltage and ESD protection of pins, used by the checks in ocdb/utils/checks
.
| Name | Type | Values | Unit | Description |
|:-----|:-----|:-------|:-----|:------------|
| generic-pin
| GenericPin()
| see below | | Properties for rated voltage and ESD protection. |
; Library property name: generic-pin
public pcb-struct ocdb/utils/property-structs/GenericPin :
max-voltage:Toleranced|RelativeVoltage ; Maximum voltage that can be applied to pin (V)
rated-esd:Double ; Rated ESD voltage (V)
; Example call
property(self.SDA.generic-pin) = GenericPin(min-max(-0.3, 3.6), 1500.0)
Name | Type | Values | Unit | Description |
---|---|---|---|---|
digital-input | DigitalInput() | see below | Properties to check digital inputs |
; Library property name: digital-input
public pcb-struct ocdb/utils/property-structs/DigitalInput :
vil:Toleranced|RelativeVoltage ; Range of voltages that will read as logic low (V)
vih:Toleranced|RelativeVoltage ; Range of voltages that will read as logic high (V)
vdd-pin:JITXObject ; Pin that powers the high reference
gnd-pin:JITXObject ; Pin that powers the low reference
leakage-current:Double ; Leakage current in Amps
; Example call
property(UART-RX.digital-input) = DigitalInput(0.4, 0.7 * vcc, VCCIO, GND, 1.0e-6)
Name | Type | Values | Unit | Description |
---|---|---|---|---|
digital-output | DigitalOutput() | see below | Properties to check digital inputs |
; Library property name: digital-output
public pcb-struct ocdb/utils/property-structs/DigitalOutput :
driver:LogicFamily ; Driver model of the output
tristateable:True|False ; Can this pin be put in a high-impedance mode?
vdd-pin:JITXObject ; Pin that powers the high reference
gnd-pin:JITXObject ; Pin that powers the low reference
; Example Call
property(UART-TX.digital-output) = DigitalOutput(CMOSOutput(min-max(0.3, 0.5), min-max(2.6, 3.3)), false, VCCIO, GND)
Name | Type | Values | Unit | Description |
---|---|---|---|---|
digital-io | DigitalIO() | see below | Properties to check digital io pins |
; Library property name: digital-io
public pcb-struct ocdb/utils/property-structs/DigitalIO :
driver:LogicFamily ; Driver model of the output
vil:Toleranced|RelativeVoltage ; Range of voltages that will read as logic low (V)
vih:Toleranced|RelativeVoltage ; Range of voltages that will read as logic high (V) gnd-pin:JITXObject ; Pin that powers the driver low reference
vdd-pin:JITXObject ; Pin that powers the high reference
gnd-pin:JITXObject ; Pin that powers the low reference
leakage-current:Double ; Leakage current in Amps
; Example call
property(PA[3].digital-io) = DigitalIO(CMOSOutput(min-max(0.0, 0.4), min-max(3.3 - 0.4, 3.3)), typ(0.3 * 3.3), typ(0.7 * 3.3), self.VDD, self.VSS, 50.0e-9)
Closest Standard Value
How to find the closest standard value for a resistor, capacitor, or inductor.
If you've computed the value you need for a resistor, capacitor, or inductor, and now you need to compute the closest normal value to that computed value, you can use closest-std-val
.
closest-std-val
closest-std-val
take a value and a tolerance as input, and return a standard/normal/nominal EIA value you can use to create or query your basic components (resistor, capacitor, or inductor):
; Returns the closest value to v in the tolerance range specified by tol
public defn closest-std-val (v:Double, tol:Double)
Example usage:
; get the required resistance, given the input voltage and the desired current
val computed-resistance = (voltage - typ(vf)) / typ(computed-current)
; turn the resistor into the closest standard value resistor by passing in a the resistance that we computed
val standard-computed-resistance = closest-std-val(typ-value(computed-resistance), 5.0)
Adding Mounting Holes
JITX contains utilities to add mounting holes to a board automatically. A single call to add-mounting-holes
can add a mounting hole to each corner of your board.
In order to add mounting holes to your board, call add-mounting-holes
with your board shape, the size of screw that you want, and optionally a Tuple which specifies the corners that you don't want holes added:
; Add mounting holes to the board given its shape, a screw size, and hole indices to exclude.
public defn add-mounting-holes (board-shape:Shape, screw-size:String|Double, no-hole:Tuple<Int>)
If you want holes added to every corner of the board:
; Add mounting holes to the board given its shape and a screw size.
public defn add-mounting-holes (board:Shape, screw-size:String|Double):
add-mounting-holes(board, screw-size, [])
Other mounting hole utilities also exist:
; Add mounting holes to the board given its shape. Defaults to M3 size holes.
public defn add-mounting-holes (board:Shape)
; Add mounting holes to the board given its shape, excluding holes given
; by the indices in no-holes. Defaults to M3 size mounting holes.
public defn add-mounting-holes (board:Shape, no-hole:Tuple<Int>)
Screw Sizes
The screw size is specified by a string, and can be any one of the following:
- "M2"
- "M2p5"
- "M3"
- "M3p5"
Testpoint - how to place a testpoint
We can place a testpoint for any net with a call to testpoint-strap
.
Here is the built-in definition of testpoint-strap:
; place a testpoint
defn testpoint-strap (tp:JITXObject, tp-name:String, diameter:Double) -> JITXObject:
It accepts a net to place the testpoint for, a name to apply to the testpoint, and the diameter you want the circular SMD test pad to be.
Example Usage
; add a testpoint for this pin
val test-pad = my-testpoint-strap(IN, net-name, 2.0) ; in current versions of OCDB, we can directly call `testpoint-strap` function
Make-Net
make-net
is another way to create connection between components. It creates the exact same type of connection that the net
statement makes.
However, make-net
allows us to name nets with strings. This tends to be useful when constructing nets inside for
loops or in recursive functions.
Signature
defn make-net (name:Symbol, refs:Seqable<JITXObject>) :
...
defn make-net (name:Symbol, type:PortType|False, refs:Seqable<JITXObject>) :
...
defn make-net (name:Symbol, type:PortType|False, refs:Seqable<JITXObject>, public?:True|False) :
...
name
- Name as aSymbol
for this nettype
- Constructs a net of a particular port type likeSinglePin
,Bundle
, orPortArray
.False
impliesSinglePin
refs
- Object capable of generating a sequence ofJITXObject
s like ports or nets - same list of refs you would pass to thenet
statement.public?
- Flag to make this net publicly accessible outside the module. By default this value isfalse
.
Example usage
pcb-module top-level:
inst host : mcu
inst sensor : temp-sensor
val name = to-string("alert-%_" % [1])
val symb = to-symbol(name)
make-net(symb, [host.GPIO[0], sensor.ALERT])
In this example, we connect a GPIO pin on the host to the ALERT
output of a sensor using the make-net
function. This is a contrived example and only intended to show
the mechanics of the make-net
function.
Symbols - set symbols of a net
We can set any net to use a specified symbol. This is especially useful for power and gnd nets.
You can design your own symbol, or you can use any of the many built-in symbols.
Example
To assign power symbols to the GND and POWER nets, we could specify the following in our top level pcb-module
:
; set symbols of some nets
symbol(GND) = ocdb/utils/symbols/ground-sym
symbol(POWER) = ocdb/utils/symbols/supply-sym
LayerIndex
The LayerIndex
type is used to specify which conductor layer in the board stackup that a particular statement or property affects.
Outline
defstruct LayerIndex :
index:Int
side:Side
defn LayerIndex (index:Int, side:Side = Top) -> LayerIndex:
...
The LayerIndex
type expects two arguments:
index
- If we consider the conductor layers in board stackup an zero-indexed array, theindex
is the offset into the array from either theTop
or theBottom
.side
- This parameter determines whether theindex
applies from theTop
going down, or from theBottom
going up.
Symmetry of Top and Bottom Indices
The Bottom
side indices can be considered a reverse indexing scheme.
Notice that there is a way to index every layer in the stackup in either direction.
Shortcuts based on Side
In some applications of the LayerIndex
type, you will often see the type LayerIndex|Side
used. This means that the user can pass either a LayerIndex
or a Side
parameter in the form of Top
or Bottom
enumerated values.
These Side
values are a shortcut for:
Top
=>LayerIndex(0, Top)
Bottom
=>LayerIndex(0, Bottom)
Operators
Currently the plus
and minus
operators are defined for the LayerIndex
and Side
types. This provides a short cut for indexing:
val inner-1 = Top + 1
println("Inner 1: %_" % [inner-1])
val inner-N-1 = Bottom - 1
println("Inner N-1: %_" % [inner-N-1])
val inner-skip = inner-1 + 2
println("Inner Skip: %_" % [inner-skip])
When run this will generate:
Inner 1: LayerIndex(1)
Inner N-1: LayerIndex(1, Bottom)
Inner Skip: LayerIndex(3)
LayerSpecifier
The LayerSpecifier
type is the base type for identifying non-copper manufacturing layers. This typically includes the silkscreen, solder mask, forbidden regions, etc. It does not include the copper layers. To identify copper layers, we use the LayerIndex type.
Derived Types
LayerSpecifier
is the parent to multiple child types. The child types defined below are what we typically use with the layer() statement.
Name | Description |
---|---|
Courtyard(s:Side) | Courtyard layer to indicate land pattern bounds. Expects a Top|Bottom argument. |
Cutout() | Cutout layer for holes and slots. Vias and through-hole pads will include their hole definitions on this layer. |
SolderMask(s:Side) | Solder mask layer. Expects a Top|Bottom argument. |
Paste(s:Side) | Paste application layer. Expects a Top|Bottom argument. |
Glue(s:Side) | Glue application layer. Expects a Top|Bottom argument. |
Finish(s:Side) | Surface Finish layer to indicate plating. Expects a Top|Bottom argument. |
Silkscreen(<br>  name:String,<br>  s:Side<br>) | Silkscreen application layer. Expects a string name and a Top|Bottom argument. |
BoardEdge() | Geometry to align with edge of board |
ForbidVia() | Forbid Vias layer (via keepout) |
ForbidCopper(<br>  start:LayerIndex,<br>  end:LayerIndex<br>) | Forbid Copper layer (copper keepout). Expects a LayerIndex argument to specify which layer[s] to forbid copper. |
CustomLayer(<br>  name:String,<br>  s:Side<br>) | Custom Mechanical layer. Expects a name and a Top|Bottom argument. |
Cutout
The Cutout
layer is intended to make internal holes in the board shape, like for through holes or routed holes/slots.
While you can use any geometry on the Cutout
layer - keep in mind your fabricator's limitations and constraints. If your fabricator only has a router (and not a laser or waterjet cutter) then they will likely not be able to construct a hole with squared corners.
Silkscreen
This layer expects a name:String
argument. This name acts like a class
attribute in HTML. It is intended to group like content together.
When the board data is exported, the silkscreen content for the top and bottom layers, respectively, will be flattened into a single top and bottom silkscreen layer.
CustomLayer
Similarly, the CustomLayer
specifier expects a name:String
argument. This argument allows the user to specify different classes of content like:
- Fabrication Notes
- Assembly Notes
- Dimensions
See the following links for how these layers map to export backends:
ForbidCopper
The ForbidCopper
specifier takes a LayerIndex instance[s] as an argument. This allows you to construct keepout regions for the copper on any layer or set of layers in a design.
defn ForbidCopper (start:LayerIndex, end:LayerIndex = start) -> ForbidCopper :
...
Notice that the end
by default is start
. If you construct a ForbidCopper()
with a single argument, then only that copper layer will possess a forbidden region.
Let's consider a case where we have an antenna on a N-layer board:
And we want to place a forbid region in the blue area of the board.
pcb-board curr-board:
...
val forbid-range = ForbidCopper(
LayerIndex(1), LayerIndex(0, Bottom)
)
layer(forbid-range) = loc(40.0, 0.0) * Rectangle(10.0, 30.0)
Here we construct a ForbidCopper
instance that spans the layer 1 to N
. Notice that we don't include the layer 0
or the Top
layer because that is where the antenna structure will go. The LayerIndex(0, Bottom)
is a means of specifying the bottom copper layer of the board without knowing the full number of layers in the stackup.
We then use the loc()
function to position the desired forbid region on the board. This takes our 10mm x 30mm
forbid region and translates it 40mm to right from the origin of the board.
Components
Sections
High Level Part Query
We can query actual parts from the JITX database that can be bought. Our database contains resistors, inductors, capacitors and STM micro-controllers.
Check the properties reference for a description of supported attributes.
Read about the Low Level Part Query API to learn about the syntax of the query requirements.
The high level functions in ocdb/utils/generic-components
such as chip-resistor make calls to less specialized functions
in ocdb/utils/parts
such as Resistor that make calls to the low level API such as dbquery-first
.
Supported parameters
The supported special query parameters are:
- _exist : List of attributes that the returned documents must contain.
- _sort : The returned documents will be sort according to the list of attributes specified
by this parameter, applied in the order they appear. Sorting in descending fashion can
be obtained by starting a sorting attribute by a minus sign "−". This can take 2 additional value besides
the list of existing properties:
area
is an alias fordimensions.area
andcost
is an alias forprice
.
Regular filter properties:
- We can filter on a part attribute :
{"category": "resistor"}
. - For filtering over capacitance, resistance and inductance, those constraints are replaced by interval allowing for queries with a precision of 0.0005Ω, 0.01A and 0.01F respectively.
- For filtering over the tolerance :
{"tolerance": 0.05}
will constrain the actual tolerance range to be included in the interval[-5%, 5%]
. Returns 500 Bas Request if this is not a positive double - We can filter on a nested parameter using
.
:{"dimensions.x": 11}
- We can filter on a list of values giving a list :
{"case": ["Axial", "0402"]}
- We can filter with an inequality prepending
min-
ormax-
:{"max−rated-power": 0.05}
.max-x
,max-y
andmax-z
are shorthands formax-dimensions.x
,max-dimensions.y
,max-dimensions.z
- We interpret
minimum_quantity
as a floor on bulk purchase size; we automatically convertminimum_quantity
tomax-minimum_quantity
.
Example:
#use-added-syntax(jitx)
defpackage my-design :
import ocdb/utils/generic-components
pcb-module my-module :
inst res : chip-resistor(["max-z" => 0.5, "_sort" => ["-rated-power"], "_exist" => ["tolerance"]])
Resistors
This sections details how to search and specify resistors in a design. There are over 1,000,000 resistors in the JITX database and the same part can be referenced in different packaging (cut tape, reels...).
chip-resistor
finds a specific resistor based on specified properties and stock.
look-up-chip-resistors
is a function that returns lists of resistors based on properties and stock.
res-strap
does everything chip-resistor
does and connects it to two nets.
gen-res-cmp
allows us to place generic resistor components for offline use.
The db-parts section describes lower level functions, and the properties section describes every available attribute we can search with.
Contents:
- ocdb/utils/generic-components
- ocdb/utils/db-parts
- Properties
- Attribute:
resistance
- Attribute:
tolerance (min, max)
- Attribute:
manufacturer
- Attribute:
mounting
- Attribute:
case
- Attribute:
type
- Attribute:
composition
- Attribute:
rated-power
- Attribute:
tcr (neg, pos)
- Attribute:
rated-temperature (min, max)
- Attribute:
mpn
- Attribute:
dimensions (x, y, z, area)
- Attribute:
stock
- Attribute:
price
- Attribute:
minimum_quantity
- Attribute:
metadata.datasheets
- Attribute:
metadata.image
- Attribute:
metadata.digi-key-part-number
- Attribute:
metadata.description
- Attribute:
metadata.series
- Attribute:
metadata.packaging
- Attribute:
metadata.features
- Attribute:
metadata.supplier-device-package
- Attribute:
metadata.number-of-terminations
- Attribute:
ocdb/utils/generic-components
The resistor functions are located in ocdb/utils/generic-components
, which imports ocdb/utils/db-parts
.
chip-resistor
We can query an actual SMD resistor from the JITX database that can be bought. This call requires internet access.
chip-resistor
calls the lower level Part Query API adding the list of attributes provided
by the user on top of design settings.
public defn chip-resistor (params:Tuple<KeyValue>)
Here are the quick accessors:
public defn chip-resistor (resistance:Double)
public defn chip-resistor (resistance:Double, tolerance:Double)
The resistance is in Ohms and the tolerance is unit-less (0.01 is ±1%).
Example:
- res1 = 10 kΩ
- res2 = 0.01 Ω, 0.5% tolerance
- res3 = 0.01 Ω, 0.5% tolerance, 0.5 Watt or more
- res4 = 1 Ω, 0.5% tolerance, 1206 size
- res5 = 100 mΩ, 0.5% tolerance, 1210 size or larger
- res6 = 100 mΩ, 0.5% tolerance, 1210 size or larger, rated to 60° C or above
- res7 = First, the value is calculated ( (5-3)/0.005 = 400 Ω), then it's rounded to the closest 5% resistor value (402 Ω), then a 402 Ω resistor of 1% tolerance is returned.
#use-added-syntax(jitx)
defpackage my-design :
import ocdb/utils/generic-components
import ocdb/utils/design-vars
; Overrides default value set in ocdb/utils/design-vars
MIN-PKG = "0402"
pcb-module my-module :
inst res1 : chip-resistor(10.0e3)
inst res2 : chip-resistor(0.01, 0.005)
inst res3 : chip-resistor(["resistance" => 0.01 "tolerance" => 0.005 "min-rated-power" => 0.5])
inst res4 : chip-resistor(["resistance" => 1.0 "tolerance" => 0.005 "case" => "1206"])
inst res5 : chip-resistor(["resistance" => 100.0e-3 "tolerance" => 0.005 "case" => get-valid-pkg-list("1210")])
inst res6 : chip-resistor(["resistance" => 0.1 "tolerance" => 0.005 "case" => get-valid-pkg-list("1210") "min-rated-temperature.max" => 60.0])
var some-voltage = 3.0
val some-current = 5.0e-3
inst res7 : chip-resistor(closest-std-val( (5.0 - some-voltage) / some-current,0.5), 0.01)
Fixed Requirements :
- category: "resistor"
- mounting: "smd"
- minimum_quantity: 1
Default design Requirements (set by global variables defined in ocdb/utils/design-vars
) :
- min-stock: 500
- _sort: ["area"]
- max-rated-temperature.min: 0.0
- min-rated-temperature.max: 25.0
- case: ["0201" "02016" "0202" "0302" "0303" "0402" "0404" "0503" "0505" "0603" "0612" "0805" "1206" "1210" "1218" "1812" "2010" "2512" "2525" "2615" "2616" "3920" "4122" "4823" "5329" "6030"]
Those design requirements can be changed, either by changing ocdb/utils/design-vars
or by importing ocdb/utils/design-vars
and
setting new values for the global variables:
- min-stock: this is 5 times
DESIGN-QUANTITY
. Which mean thatDESIGN-QUANTITY
default is 100. - _sort: set by
OPTIMIZE-FOR
. Default is ["area"]. - [max-rated-temperature.min min-rated-temperature.max] : rated-temperature range in degC set by
OPERATING-TEMPERATURE
. - case: computed from
MIN-PKG
.MIN-PKG
default is "0201".
look-up-chip-resistors
public defn look-up-chip-resistors (attribute: String) -> Tuple
public defn look-up-chip-resistors (attribute: String, filter-properties:Tuple<KeyValue>) -> Tuple
Looks up the list of available values (at most 1000 returned) for attribute
amongst chip resistors in the JITX database.
This call filters on the same properties as chip-resistor. Additional properties filter-properties
can
be given in argument to restrict further criteria on the considered chip resistors.
Example:
$ jitx repl
stanza> import ocdb/utils/generic-components
stanza> println $ look-up-chip-resistors("type", ["resistance" => 1.0, "min-rated-power" => 1.0])
["chip"]
stanza> println $ look-up-chip-resistors("composition", ["resistance" => 1.0, "min-rated-power" => 1.0])
["thick-film" "thin-film" "wirewound"]
res-strap
res-strap
is a wrapper around chip-resistor that also connects the resistor to two nets specified. This call requires internet access.
It needs to be called inside a pcb module.
public defn res-strap (first-pin:JITXObject, second-pin:JITXObject, resistance:Double)
public defn res-strap (first-pin:JITXObject, second-pin:JITXObject, resistance:Double, tol:Double)
public defn res-strap (first-pin:JITXObject, second-pin:JITXObject, params:Tuple<KeyValue>)
Example:
#use-added-syntax(jitx)
defpackage my-design :
import ocdb/utils/generic-components
pcb-module my-module :
port gnd
port power-5v
port signal
res-strap(power-5v, signal, 10.0e3) ; 10 kΩ between power-5v and signal
res-strap(gnd, signal, 120.0, 0.01) ; 120 Ω, 1% tolerance between gnd and signal
res-strap(power-5v, signal, ["resistance" => 0.01 "tolerance" => 0.005 "min-rated-power" => 0.5]) ; 0.01 Ω, 1% tolerance, 0.5 Watt or more between power-5v and signal
gen-res-cmp
This is a generic resistor component, this is not an actual part that can be bought. This call does not require internet access.
Pins are p[1]
and p[2]
.
Arguments:
r-type := ResistorStd | ResistorVariable | ResistorPot | ResistorPhoto | ResistorTherm
fromocdb/utils/ResistorSymbolType
(decides the schematic symbol)res
: resistance in Ohmstol
: tolerance (1.0 means ±1%)pwr
: maximum power in wattspkg-name
: Package name (example: "0204")
public defn gen-res-cmp (res:Double)
public defn gen-res-cmp (res:Double, pkg:String)
public defn gen-res-cmp (res:Double, tol:Double)
public defn gen-res-cmp (res:Double, tol:Double, pkg:String)
public defn gen-res-cmp (res:Double, tol:Double, pwr:Double)
public pcb-component gen-res-cmp (r-type:ResistorSymbolType, res:Double, tol:Double, pwr:Double, pkg-name:String)
Here are the quick accessors: Defaults used are:
r-type
:ResistorStd
tol
: 2%pwr
: 0.0625 Wpkg-name
: "0402"
Valid entries for ResistorSymbolType
:
ResistorStd
ResistorVariable
ResistorPot
ResistorPhoto
ResistorTherm
Specifying ResistorSymbolType
requires importing ocdb/utils/symbols
Example:
- r = instantiating a 5Ω generic resistor
- r1 = instantiating a 5Ω generic resistor with a 1206 package
- r2 = instantiating a 120Ω photo resistor, 10% tolerance, 0.5W, with a 0805 package
pcb-module my-design :
inst r : gen-res-cmp(5.0)
inst r1 : gen-res-cmp(5.0, "1206")
inst r2 : gen-res-cmp (ResistorPhoto, 120.0, 0.10, 0.5, "0805")
ocdb/utils/db-parts
Resistor Struct
Here is the Resistor
struct. When resistors are queried from the JITX database, the resistor data gets populated into this struct.
It can be used to write pure stanza solvers like a voltage divider (see open-components-database/modules/voltage-divider.stanza
)
without having to deal with jitx macros.
public defstruct Resistor <: Component :
; Generic properties
manufacturer: String
mpn: String
trust: String
x: Double with: (as-method => true)
y: Double with: (as-method => true)
z: Double|False
mounting: String
rated-temperature: MinMaxRange|False
case: String|False
sourcing: Sourcing
metadata: Tuple<KeyValue>
; Resistor specific properties
type: String ; Type of resistor
tolerance: MinMaxRange|False ; Guaranteed tolerance from manufacture (Ohm/Ohm)
resistance: Double ; Nominal resistance (Ohm)
composition: String|False ; Composition of resistor
rated-power: Double|False ; Power dissipation limit as rated by manufacturer. One value, or PWL function (W | [degC, W])
TCR: TCR|False ; Temperature coefficient of resistance (ohms/ohm*degC)
sellers: Tuple<Seller>|False with: (as-method => true)
resolved-price: Double|False with: (as-method => true)
public defstruct MinMaxRange :
min: Double
max: Double
defstruct Sourcing :
price: Double|False
minimum-quantity: Int
stock: Int
public defstruct TCR :
reference-temperature: Double
positive: Double
negative: Double
defmethod to-jitx (resistor: Resistor) -> Instantiable
Takes a Resistor
struct and returns an instance.
Example:
#use-added-syntax(jitx)
defpackage my-design :
import ocdb/utils/db-parts
val resistor = Resistor(["tolerance" => 0.05 "composition" => "thick-film"])
pcb-module my-module :
inst res : to-jitx(resistor)
Resistor Accessors
We can query an actual resistor from the JITX database that can be bought. This call requires internet access.
Resistor
calls the lower level Part Query API adding the list of attributes provided
by the user on top of design settings.
public defn Resistor (properties:Tuple<KeyValue>) -> Resistor
Fixed Requirements :
- category: "resistor"
Default design Requirements (set by global variables defined in ocdb/utils/design-vars
) :
- _sort: ["area"]
- max-rated-temperature.min: 0.0
- min-rated-temperature.max: 25.0
Those design requirements can be changed, either by changing ocdb/utils/design-vars
or by importing ocdb/utils/design-vars
and
setting new values for the global variables:
- _sort: set by
OPTIMIZE-FOR
. - [max-rated-temperature.min min-rated-temperature.max] : rated-temperature range in degC set by
OPERATING-TEMPERATURE
.
Here are accessors to override design requirements:
public defn Resistor (properties:Tuple<KeyValue>,
exist:Tuple<String>) -> Resistor
public defn Resistor (properties:Tuple<KeyValue>,
exist:Tuple<String>,
sort:Tuple<String>,
operating-temperature:Toleranced|False) -> Resistor
Arguments:
exist
: we can require the resulting resistor to have an attribute that is otherwise optional (for example "tolerance").sort
: overrides the sorting arguments that would otherwise beOPTIMIZE-FOR
.operating-temperature
: overrides the rated temperature range that would otherwise beOPERATING-TEMPERATURE
.
Example:
Query a resistor with real-time sourcing data
$ jitx repl
stanza> import ocdb/utils/db-parts
stanza> import jitx/commands
stanza> val resistor = Resistor(["resistance" => 1.0, "_sellers" => bom-vendors(), "_stock" => 1000])
stanza> println $ resistor
Resistor(
mpn = ERJ-XGNJ1R0Y
trust = low
(x, y, z) = (0.4, 0.2, 0.15)
mounting = smd
rated-temperature = MinMaxRange(min=-55.0, max=125.0)
case = 01005
type = chip
tolerance = MinMaxRange(min=-0.05, max=0.05)
resistance = 1.0
composition = thick-film
rated-power = 0.03
TCR = TCR(positive=-0.0001, negative=0.0006)
sourcing = ESR(price=0.01932, minimum-quantity=20000, stock=0)
metadata =
"datasheets" => "https://b2b-api.panasonic.eu/file_stream/pids/fileversion/1242"
"image" => "//media.digikey.com/Photos/Panasonic Photos/MFG_ERJ-XGNJ1R0Y.jpg"
"digi-key-part-number" => "10-ERJ-XGNJ1R0YTR-ND"
"description" => "RES SMD 1.0 OHM 5% 1/32W 01005"
"factory-stock" => 0.0
"qty" => 0.0
"packaging" => "Tape & Reel (TR)"
"series" => "ERJ-XGN"
"supplier-device-package" => 1005.0
"number-of-terminations" => 2.0
sellers =
Seller(
company-name = Digi-Key
resolved-price = 0.02484
offers = (
SellerOffer(
inventory-level = 17314
prices = (
SellerOfferPrice(quantity = 1, converted-price = 0.18)
SellerOfferPrice(quantity = 10, converted-price = 0.152)
SellerOfferPrice(quantity = 100, converted-price = 0.0593)
SellerOfferPrice(quantity = 1000, converted-price = 0.02484)
SellerOfferPrice(quantity = 2500, converted-price = 0.02277)
SellerOfferPrice(quantity = 5000, converted-price = 0.0207)))
SellerOffer(
inventory-level = 17314
prices = (
SellerOfferPrice(quantity = 1, converted-price = 0.18)
SellerOfferPrice(quantity = 10, converted-price = 0.152)
SellerOfferPrice(quantity = 100, converted-price = 0.0593)
SellerOfferPrice(quantity = 1000, converted-price = 0.02484)
SellerOfferPrice(quantity = 2500, converted-price = 0.02277)
SellerOfferPrice(quantity = 5000, converted-price = 0.0207)))
SellerOffer(
inventory-level = 0
prices = (
SellerOfferPrice(quantity = 20000, converted-price = 0.01932)))))
Seller(
company-name = Newark
resolved-price = 0.074
offers = (
SellerOffer(
inventory-level = 19850
prices = (
SellerOfferPrice(quantity = 1, converted-price = 0.55)
SellerOfferPrice(quantity = 25, converted-price = 0.461)
SellerOfferPrice(quantity = 100, converted-price = 0.179)
SellerOfferPrice(quantity = 250, converted-price = 0.144)
SellerOfferPrice(quantity = 500, converted-price = 0.109)
SellerOfferPrice(quantity = 1000, converted-price = 0.074)))
SellerOffer(
inventory-level = 0
prices = (
SellerOfferPrice(quantity = 20000, converted-price = 0.041)))))
Seller(
company-name = Avnet
resolved-price = 0.074
offers = (
SellerOffer(
inventory-level = 19850
prices = (
SellerOfferPrice(quantity = 1, converted-price = 0.55)
SellerOfferPrice(quantity = 25, converted-price = 0.461)
SellerOfferPrice(quantity = 100, converted-price = 0.179)
SellerOfferPrice(quantity = 250, converted-price = 0.144)
SellerOfferPrice(quantity = 500, converted-price = 0.109)
SellerOfferPrice(quantity = 1000, converted-price = 0.074)))
SellerOffer(
inventory-level = 0
prices = (
SellerOfferPrice(quantity = 20000, converted-price = 0.04914)
SellerOfferPrice(quantity = 40000, converted-price = 0.04662)
SellerOfferPrice(quantity = 80000, converted-price = 0.0441)
SellerOfferPrice(quantity = 120000, converted-price = 0.04158)
SellerOfferPrice(quantity = 160000, converted-price = 0.03906)
SellerOfferPrice(quantity = 200000, converted-price = 0.03654)
SellerOfferPrice(quantity = 2000000, converted-price = 0.03402)))))
Seller(
company-name = Arrow Electronics
resolved-price = 0.0463
offers = (
SellerOffer(
inventory-level = 20000
prices = (
SellerOfferPrice(quantity = 1, converted-price = 0.3073)
SellerOfferPrice(quantity = 10, converted-price = 0.2577)
SellerOfferPrice(quantity = 25, converted-price = 0.2551)
SellerOfferPrice(quantity = 50, converted-price = 0.2526)
SellerOfferPrice(quantity = 100, converted-price = 0.1516)
SellerOfferPrice(quantity = 250, converted-price = 0.0993)
SellerOfferPrice(quantity = 500, converted-price = 0.0924)
SellerOfferPrice(quantity = 1000, converted-price = 0.0463)
SellerOfferPrice(quantity = 3000, converted-price = 0.0423)
SellerOfferPrice(quantity = 6000, converted-price = 0.0419)
SellerOfferPrice(quantity = 15000, converted-price = 0.0324)))))
resolved-price = 0.02484)
public defn Resistors (user-properties:Tuple<KeyValue>, limit: Int) -> Tuple<Resistor>
Similar to Resistor
but querying up to 25 resistors.
Resistor from JSON
public defn Resistor (raw-json: JObject) -> Resistor
Creates a Resistor
from a JObject
.
Example:
$ jitx repl
stanza> import jitx/commands
stanza> import ocdb/utils/db-parts
stanza> import json
stanza> val jobject = dbquery-first(["category" => "resistor" "resistance" => 2.0]) as JObject
stanza> println(jobject)
JObject(entries = ["_id" => "0f8e320e1bc955ff59439bfc" "trust" => "low" "category" => "resistor" "mpn" => "OK20G5E-R52" "mounting" => "through-hole" "manufacturer" => "Ohmite" "type" => "through-hole" "dimensions" => JObject(entries = ["x" => 6.8 "y" => 2.5 "area" => 17.0]) "stock" => 0.0 "minimum_quantity" => 50000.0 "metadata" => JObject(entries = ["datasheets" => "http://www.ohmite.com/assets/docs/res_little_rebel.pdf?r=false" "digi-key-part-number" => "OK20G5E-R52-ND" "description" => "RES 2 OHM 5% 1/4W AXIAL" "factory-stock" => 0.0 "qty" => 0.0 "packaging" => "Tape & Reel (TR)" "series" => "Little Rebel® OK" "supplier-device-package" => "Axial" "number-of-terminations" => 2.0]) "price" => 0.01197 "tolerance" => JObject(entries = ["min" => -0.05 "max" => 0.05]) "resistance" => 2.0 "rated-power" => 0.25 "composition" => "carbon-film" "tcr" => JObject(entries = ["pos" => -0.00035 "neg" => 0.00035]) "case" => "Axial" "update_date" => "2021-09-04T01:26:50.670000"])
stanza> val resistor = Resistor(jobject)
stanza> println(resistor)
Resistor(
mpn = OK20G5E-R52
trust = low
(x, y, z) = (6.8, 2.5, false)
mounting = through-hole
rated-temperature = false
case = Axial
type = through-hole
tolerance = MinMaxRange(min=-0.05, max=0.05)
resistance = 2.0
composition = carbon-film
rated-power = 0.25
TCR = TCR(positive=-0.00035, negative=0.00035)
sourcing = ESR(price=0.01197, minimum-quantity=50000, stock=0)
metadata =
"datasheets" => "http://www.ohmite.com/assets/docs/res_little_rebel.pdf?r=false"
"digi-key-part-number" => "OK20G5E-R52-ND"
"description" => "RES 2 OHM 5% 1/4W AXIAL"
"factory-stock" => 0.0
"qty" => 0.0
"packaging" => "Tape & Reel (TR)"
"series" => "Little Rebel® OK"
"supplier-device-package" => "Axial"
"number-of-terminations" => 2.0)
query-available-resistance-values
public defn query-available-resistance-values (properties:Tuple<KeyValue>, exist:Tuple<String>) -> Tuple<Double> :
We can query the list of available resistance values available using the same design requirements as Resistor
, filtering on a list of query parameters properties
and requiring the
attributes in exist
to exist on the resistors.
Example:
$ jitx repl
stanza> import ocdb/utils/db-parts
stanza> println $ query-available-resistance-values(["composition" => "thick-film" "case" => "Axial"], ["tolerance"])
[100.0 150.0 270.0 470.0 511.0 560.0 680.0 750.0 1000.0 1010.0 1500.0 2000.0 2700.0 3300.0 4300.0 4700.0 4990.0 5100.0 6800.0 7500.0 10000.0 15000.0 18000.0 20000.0 24000.0 27000.0 33000.0 40000.0 47000.0 49900.0 50000.0 51000.0 60000.0 66000.0 75000.0 95300.0 100000.0 120000.0 150000.0 196000.0 200000.0 220000.0 250000.0 270000.0 300000.0 330000.0 350000.0 390000.0 400000.0 500000.0 680000.0 820000.0 1000000.0 1050000.0 1100000.0 1240000.0 1470000.0 1800000.0 2000000.0 2200000.0 2400000.0 2700000.0 3000000.0 3300000.0 4000000.0 4700000.0 5000000.0 5100000.0 6000000.0 6650000.0 7000000.0 7500000.0 8000000.0 8450000.0 9000000.0 10000000.0 11000000.0 12000000.0 12500000.0 14700000.0 15000000.0 16000000.0 20000000.0 22000000.0 22100000.0 25000000.0 27000000.0 30000000.0 30100000.0 33000000.0 39000000.0 40000000.0 47000000.0 50000000.0 60000000.0 68000000.0 70000000.0 75000000.0 80000000.0 82000000.0 100000000.0 120000000.0 130000000.0 140000000.0 150000000.0 180000000.0 195000000.0 200000000.0 222000000.0 250000000.0 300000000.0 330000000.0 400000000.0 430000000.0 470000000.0 500000000.0 750000000.0 900000000.0 1000000000.0 1200000000.0 1240000000.0 1320000000.0 2000000000.0 3000000000.0 4700000000.0 5000000000.0 9000000000.0 10000000000.0 15000000000.0 20000000000.0 50000000000.0 60000000000.0 100000000000.0 120000000000.0 200000000000.0 300000000000.0 500000000000.0]
look-up-resistors
public defn look-up-resistors (attribute: String) -> Tuple
public defn look-up-resistors (attribute: String, filter-properties:Tuple<KeyValue>) -> Tuple
Looks up the list of available values (at most 1000 returned) for attribute
amongst resistors in the JITX database.
This call filters on the same properties as Resistor. Additional properties filter-properties
can
be given in argument to restrict further criteria on the considered resistors.
Example:
$ jitx repl
stanza> import ocdb/utils/db-parts
stanza> println $ look-up-resistors("mounting", ["resistance" => 1.0, "min-rated-power" => 1.0])
["smd" "through-hole"]
Properties
Each resistor has a different Digi-Key Part Number but an mpn has typically 3 Digi-Key Part Numbers for 3 different packagings.
For example the resistor of mpn "ERJ-XGNJ1R0Y" appears with the following Digi-Key Part Numbers and packagings:
- 10-ERJ-XGNJ1R0YCT-ND: Cut Tape (CT)
- 10-ERJ-XGNJ1R0YDKR-ND: Digi-Reel®
- 10-ERJ-XGNJ1R0YTR-ND: Tape & Reel (TR)
This information can be found in the attributes metadata.digi-key-part-number
and metadata.packaging
but cannot be queried on.
We can check by ourselves doing:
$ jitx repl
stanza> import ocdb/utils/db-parts
stanza> do(println, Resistors(["mpn" => "ERJ-XGNJ1R0Y"], 25))
Check the properties reference for a description of supported attributes.
Here are available attribute values with default design requirements as of 10/14/2021. They can be queried anytime with:
$ jitx repl
stanza> import ocdb/utils/db-parts
stanza> for attribute in ["manufacturer", "mpn", "resistance", "trust", "dimensions", "mounting", "case", "stock", "price", "minimum_quantity", "type", "composition", "rated-power", "tcr", "metadata.datasheets", "metadata.image", "metadata.digi-key-part-number", "metadata.description", "metadata.packaging", "metadata.series", "metadata.features", "metadata.supplier-device-package", "metadata.number-of-terminations"] do :
> val values = look-up-resistors(attribute)
> if length(values) <= 150 :
> println("| %_ | %@ |" % [attribute, values])
> else :
> println("| %_ | %_ values |" % [attribute, length(values)])
stnaza> import json
stanza> for attribute in ["rated-temperature", "tolerance"] do :
> val values = to-tuple $ filter({_ is JObject}, look-up-resistors(attribute))
> if length(values) <= 150 :
> println("| %_ (min, max) | %@ |" % [attribute, map({"(%_, %_)" % [_0["min"], _0["max"]]}, values)])
> else :
> println("| %_ | %_ values |" % [attribute, length(values)])
stanza> for attribute in ["tcr"] do :
> val values = to-tuple $ filter({_ is JObject}, look-up-resistors(attribute))
> if length(values) <= 150 :
> println("| %_ (neg, pos) | %@ |" % [attribute, map({"(%_, %_)" % [_0["neg"], _0["pos"]]}, values)])
> else :
> println("| %_ | %_ values |" % [attribute, length(values)])
Metadata values are not sanitized.
Attribute: resistance
Values:
0.0 0.0001 0.0002 0.00025 0.0003 0.0004 0.0005 0.0007 0.00075 0.0008 0.001 0.0013 0.0015 0.002 0.0022 0.00225 0.0025 0.0028 0.003 0.0032 0.0033 0.0035 0.0036 0.00375 0.004 0.0045 0.0047 0.0048 0.005 0.006 0.0068 0.007 0.0075 0.008 0.009 0.0093 0.01 0.011 0.012 0.0125 0.013 0.0133 0.014 0.015 0.016 0.0162 0.0165 0.0169 0.017 0.0174 0.0178 0.018 0.0182 0.0187 0.019 0.0191 0.0196 0.02 0.0205 0.021 0.0215 0.022 0.0221 0.0226 0.023 0.0232 0.0237 0.024 0.0243 0.0249 0.025 0.0255 0.026 0.0261 0.0267 0.027 0.0274 0.028 0.0287 0.029 0.0294 0.03 0.0301 0.0309 0.0316 0.032 0.0324 0.033 0.0332 0.034 0.0348 0.035 0.0357 0.036 0.0365 0.0374 0.038 0.0383 0.039 0.0392 0.04 0.0402 0.0412 0.042 0.0422 0.043 0.0432 0.0442 0.045 0.0453 0.046 0.0464 0.047 0.0475 0.0487 0.049 0.0499 0.05 0.0502 0.051 0.0511 0.0523 0.0536 0.054 0.0549 0.055 0.056 0.0562 0.0576 0.059 0.06 0.0604 0.0619 0.062 0.0634 0.064 0.0649 0.065 0.066 0.0665 0.067 0.068 0.0681 0.0698 0.07 0.0715 0.0732 0.075 0.076 0.0768 0.077 0.078 0.0787 0.079 0.08 0.0806 0.081 0.082 0.0825 0.083 0.0845 0.085 0.0866 0.087 0.0887 0.089 0.09 0.0909 0.091 0.0931 0.095 0.0953 0.0976 0.1 0.101 0.102 0.105 0.107 0.11 0.113 0.115 0.118 0.12 0.121 0.124 0.125 0.127 0.13 0.132 0.133 0.135 0.137 0.14 0.142 0.143 0.145 0.147 0.149 0.15 0.151 0.152 0.154 0.156 0.158 0.16 0.162 0.165 0.166 0.167 0.169 0.17 0.174 0.176 0.178 0.18 0.182 0.187 0.191 0.196 0.2 0.205 0.21 0.215 0.22 0.221 0.226 0.229 0.23 0.232 0.234 0.237 0.24 0.243 0.249 0.25 0.252 0.255 0.258 0.26 0.261 0.264 0.267 0.27 0.271 0.274 0.28 0.287 0.294 0.3 0.301 0.309 0.31 0.316 0.32 0.322 0.324 0.33 0.332 0.333 0.34 0.348 0.35 0.357 0.36 0.365 0.37 0.374 0.375 0.38 0.383 0.39 0.391 0.392 0.397 0.4 0.402 0.407 0.41 0.412 0.42 0.422 0.427 0.43 0.432 0.44 0.442 0.449 0.45 0.453 0.46 0.464 0.466 0.47 0.475 0.481 0.487 0.49 0.499 0.5 0.5003 0.505 0.51 0.511 0.512 0.52 0.523 0.53 0.536 0.54 0.549 0.55 0.556 0.557 0.56 0.562 0.57 0.576 0.58 0.59 0.597 0.598 0.6 0.604 0.61 0.619 0.62 0.625 0.626 0.63 0.634 0.635 0.64 0.645 0.649 0.65 0.66 0.665 0.67 0.673 0.68 0.681 0.689 0.69 0.698 0.7 0.71 0.715 0.723 0.732 0.74 0.741 0.75 0.751 0.752 0.759 0.768 0.78 0.781 0.787 0.796 0.8 0.806 0.82 0.825 0.83 0.833 0.84 0.845 0.85 0.853 0.865 0.866 0.87 0.88 0.887 0.898 0.9 0.909 0.91 0.931 0.942 0.95 0.953 0.96 0.965 0.976 0.99 1.0 1.0009 1.001 1.0015 1.005 1.0055 1.01 1.02 1.024 1.03 1.04 1.05 1.07 1.09 1.1 1.125 1.13 1.1364 1.15 1.152 1.16 1.18 1.2 1.21 1.212 1.222 1.23 1.24 1.25 1.26 1.2626 1.27 1.29 1.3 1.32 1.33 1.35 1.37 1.4 1.42 1.43 1.44 1.47 1.5 1.505 1.52 1.53 1.54 1.56 1.58 1.592 1.6 1.62 1.64 1.65 1.6667 1.6696 1.67 1.68 1.69 1.7 1.74 1.78 1.79 1.8 1.82 1.85 1.87 1.9 1.91 1.93 1.954 1.96 1.98 2.0 2.0015 2.002 2.01 2.015 2.02 2.04 2.05 2.07 2.08 2.085 2.088 2.1 2.138 2.15 2.151 2.195 2.2 2.21 2.23 2.25 2.26 2.29 2.3 2.304 2.3047 2.32 2.341 2.35 2.37 2.4 2.43 2.46 2.47 2.49 2.5 2.5065 2.507 2.51 2.52 2.55 2.58 2.6 2.61 2.639 2.64 2.6435 2.656 2.66 2.67 2.7 2.71 2.74 2.75 2.77 2.8 2.82 2.87 2.88 2.92 2.94 2.98 3.0 3.01 3.03 3.05 3.06 3.09 3.1 3.11 3.135 3.15 3.16 3.18 3.2 3.24 3.28 3.3 3.32 3.329 3.3333 3.34 3.343 3.36 3.38 3.4 3.47 3.48 3.5 3.52 3.54 3.57 3.5728 3.6 3.61 3.65 3.7 3.72 3.74 3.75 3.79 3.8 3.83 3.84 3.88 3.9 3.907 3.92 3.97 3.999 4.0 4.008 4.01 4.02 4.022 4.07 4.1 4.12 4.16 4.17 4.18 4.185 4.19 4.2 4.22 4.3 4.32 4.42 4.444 4.48 4.49 4.5 4.53 4.54 4.57 4.64 4.651 4.6512 4.7 4.71 4.74 4.75 4.761 4.78 4.8 4.81 4.87 4.9 4.93 4.95 4.99 5.0 5.005 5.01 5.012 5.025 5.05 5.08 5.1 5.11 5.15 5.17 5.19 5.2 5.21 5.23 5.263 5.3 5.36 5.4 5.487 5.49 5.5 5.56 5.6 5.62 5.69 5.7 5.71 5.714 5.7143 5.75 5.76 5.8 5.83 5.85 5.9 5.92 5.97 6.0 6.02 6.04 6.06 6.12 6.19 6.2 6.23 6.25 6.3 6.34 6.3407 6.35 6.4 6.42 6.43 6.453 6.49 6.5 6.54 6.57 6.6 6.64 6.65 6.666 6.6666 6.6667 6.67 6.7 6.73 6.78 6.8 6.81 6.85 6.9 6.91 6.926 6.98 7.0 7.029 7.06 7.15 7.2 7.23 7.3 7.32 7.37 7.3785 7.41 7.43 7.5 7.51 7.59 7.6 7.68 7.77 7.8 7.87 7.96 8.0 8.045 8.06 8.09 8.1 8.16 8.2 8.21 8.22 8.25 8.3 8.333 8.3333 8.4 8.45 8.5 8.6 8.66 8.7 8.76 8.8 8.85 8.87 8.98 9.0 9.09 9.1 9.2 9.31 9.4 9.42 9.45 9.47 9.5 9.53 9.6 9.65 9.7 9.74 9.76 9.88 9.95 9.995 9.999 10.0 10.001 10.01 10.015 10.03 10.1 10.12 10.2 10.21 10.3 10.4 10.417 10.5 10.6 10.7 10.8 10.85 10.86 10.9 10.973 11.0 11.1 11.11 11.111 11.16 11.2 11.25 11.3 11.4 11.5 11.52 11.6534 11.66 11.7 11.8 11.9 12.0 12.1 12.106 12.2 12.3 12.4 12.5 12.584 12.6 12.7 12.8 12.9 12.944 13.0 13.2 13.28 13.3 13.333 13.4 13.5 13.6 13.7 13.8 13.851 13.889 14.0 14.1 14.142 14.16 14.2 14.204 14.285 14.3 14.4 14.42 14.5 14.53 14.55 14.59 14.7 14.9 15.0 15.05 15.1 15.17 15.2 15.3 15.4 15.446 15.491 15.6 15.7 15.8 15.92 16.0 16.032 16.09 16.18 16.2 16.32 16.4 16.48 16.5 16.65 16.667 16.7 16.8 16.83 16.9 16.95 17.0 17.2 17.238 17.4 17.48 17.5 17.6 17.8 17.857 17.9 18.0 18.1 18.2 18.3 18.4 18.5 18.7 18.75 18.8 18.9 19.0 19.1 19.17 19.2 19.3 19.42 19.5 19.53125 19.57 19.6 19.8 19.9 20.0 20.002 20.02 20.1 20.2 20.27 20.3 20.5 20.552 20.6 20.8 20.833 21.0 21.1 21.17 21.2 21.27 21.298 21.3 21.42 21.5 21.574 21.6 21.7 21.8 21.946 22.0 22.1 22.2 22.239 22.3 22.5 22.6 22.81 22.9 23.0 23.2 23.331 23.4 23.7 23.8 23.81 24.0 24.001 24.2 24.224 24.3 24.5 24.6 24.72 24.9 24.92 25.0 25.1 25.1252 25.2 25.3 25.4 25.5 25.7 25.8 26.0 26.1 26.2 26.4 26.5 26.5258 26.56 26.64 26.7 26.9 27.0 27.1 27.23 27.3 27.4 27.5 27.6 27.7 27.702 27.9 28.0 28.095 28.1 28.2 28.242 28.3 28.4 28.5 28.7 28.7708 28.771 28.8 28.904 28.948 29.0 29.1 29.2 29.247 29.4 29.7 29.8 30.0 30.001 30.1 30.2 30.3 30.303 30.5
Attribute: tolerance (min, max)
Values:
(-0.3, 0.0) (-0.3, 0.3) (-0.2, 0.2) (-0.15, 0.15) (-0.1, 0.0) (-0.1, 0.1) (-0.05, 0.05) (-0.03, 0.03) (-0.02, 0.02) (-0.01, 0.01) (-0.005, 0.005) (-0.0025, 0.0025) (-0.002, 0.002) (-0.001, 0.001) (-0.0005, 0.0005) (-0.0002, 0.0002) (-0.0001, 0.0001) (-5.0e-05, 5.0e-05) (-2.5e-05, 2.5e-05) (-2.0e-05, 2.0e-05) (-1.0e-05, 1.0e-05)
Attribute: manufacturer
Values:
"AVX Corporation" "Aillen" "American Technical Ceramics" "Anaren" "Bourns Inc." "CAL-CHIP ELECTRONICS, INC." "CTS Resistor Products" "Caddock Electronics Inc." "Delta Electronics/Cyntec" "KOA Speer Electronics, Inc." "Kamaya Inc." "Meritek" "Murata Power Solutions Inc." "NTE Electronics, Inc" "Ohmite" "Panasonic Electronic Components" "Riedon" "Rohm Semiconductor" "Samsung Electro-Mechanics" "Stackpole Electronics Inc" "Susumu" "TE Connectivity" "TE Connectivity AMP Connectors" "TE Connectivity Passive Product" "TT Electronics/IRC" "TT Electronics/Welwyn" "TubeDepot" "Venkel" "Viking Tech" "Vishay Beyschlag/Draloric/BC Components" "Vishay Dale" "Vishay Dale Thin Film" "Vishay Electro-Films" "Vishay Foil Resistors (Division of Vishay Precision Group)" "Vishay Huntington Electric Inc." "Vishay Sfernice" "Vishay Thin Film" "Walsin Technology Corporation" "Würth Elektronik" "Xicon" "YAGEO" "Yageo"
Attribute: mounting
Values:
"smd" "through-hole"
Attribute: case
Values:
"009005" "01005" "0201" "02016" "0202" "0302" "0402" "0404" "0505" "0603" "0612" "0805" "1206" "1210" "1218" "1812" "2-SMD, J-Lead" "2010" "2010 J-Lead" "2012 J-Lead" "2015" "2512" "2512 J-Lead" "2515 J-Lead" "2520" "2615" "2615 J-Lead" "2616" "2817" "3014 J-Lead" "3017 J-Lead" "3916 J-Lead" "3920" "4-ESIP" "4-SIP" "4122" "4122 J-Lead" "4124 J-Lead" "4318 J-Lead" "4324 J-Lead" "4524 J-Lead" "4525 J-Lead" "4527 J-Lead" "5025 J-Lead" "5322 J-Lead" "5329" "6030" "6227 J-Lead" "6327 J-Lead" "6927 J-Lead" "8035 J-Lead" "8127 J-Lead" "8230 J-Lead" "Axial" "Axial - 4 Leads" "Axial, Radial" "Axial, Radial Bend" "Axial, Radial Formed" "MELF, 0102" "MELF, 0204" "MELF, 0207" "MELF, 0309" "Non-Standard D-PAK" "Nonstandard" "PFC10" "Radial" "Radial - 2 Leads" "Radial - 4 Leads" "Radial, Tubular" "SMD, J-Bend" "SMD, L-Bend" "SR10" "SR20" "Strip, C Bend" "Strip, L Bend" "TO-126-2" "TO-204AA" "TO-220-2" "TO-220-2 Full Pack" "TO-220-4" "TO-220-4, SMD" "TO-247-2" "TO-247-2 Variant" "TO-252-3, DPak" "TO-263-3, D²Pak" "Wide 0402" "Wide 0603" "Wide 0604" "Wide 0805" "Wide 1206" "Wide 1508" "Wide 1612" "Wide 1812" "Wide 2010" "Wide 2412" "Wide 2512" "Wide 2516" "Wide 2827" "Wide 3008" "Wide 3015" "Wide 3518" "Wide 4320" "Wide 5929"
Attribute: type
Values:
"chip" "through-hole"
Attribute: composition
Values:
"carbon-composition" "carbon-film" "ceramic" "metal-element" "metal-film" "metal-foil" "metal-oxide-film" "thick-film" "thin-film" "wirewound"
Attribute: rated-power
Values:
0.0125 0.0189 0.02 0.025 0.03 0.0375 0.05 0.06 0.063 0.075 0.1 0.12 0.125 0.135 0.15 0.16 0.167 0.175 0.2 0.225 0.245 0.25 0.3 0.32 0.333 0.35 0.375 0.4 0.5 0.6 0.63 0.667 0.7 0.75 0.8 0.9 1.0 1.1 1.2 1.25 1.3 1.33 1.4 1.5 1.6 1.75 1.8 2.0 2.16 2.2 2.3 2.4 2.5 2.7 2.75 2.8 2.88 3.0 3.12 3.2 3.25 3.47 3.5 3.6 3.75 3.8 4.0 4.2 4.25 4.32 4.4 4.5 4.8 5.0 5.2 5.5 5.6 6.0 6.25 6.3 6.5 7.0 7.5 7.8 8.0 8.4 8.8 9.0 10.0 11.0 12.0 12.5 13.0 13.5 14.0 15.0 16.0 16.8 17.0 18.0 20.0 22.0 22.4 25.0 28.0 30.0 35.0 36.0 40.0 45.0 50.0 60.0 75.0 95.0 100.0 140.0 150.0
Attribute: tcr (neg, pos)
Values:
(0.0039, -0.0039) (0.002, -0.002) (0.0018, -0.0018) (-0.0015, -0.0015) (-0.0009, -0.0015) (0.0015, -0.0015) (-0.0013, -0.0013) (0.0013, -0.0013) (-0.0006, -0.0012) (0.0012, -0.0012) (0.0011, -0.0011) (-0.0016, -0.001) (0.001, -0.001) (0.00095, -0.00095) (0.0009, -0.0009) (0.00085, -0.00085) (0.0008, -0.0008) (0.00075, -0.00075) (-0.0007, -0.0007) (0.0004, -0.0007) (0.0007, -0.0007) (0.00065, -0.00065) (0.0002, -0.0006) (0.0006, -0.0006) (0.0003, -0.0005) (0.00035, -0.0005) (0.0005, -0.0005) (0.00035, -0.00045) (0.00045, -0.00045) (0.00015, -0.0004) (0.0004, -0.0004) (0.00035, -0.00035) (0.0005, -0.00035) (0.000325, -0.000325) (5.0e-05, -0.0003) (0.0003, -0.0003) (0.0005, -0.0003) (0.00028, -0.00028) (0.000275, -0.000275) (0.00027, -0.00027) (0.00026, -0.00026) (0.00025, -0.00025) (0.0005, -0.00025) (0.000245, -0.000245) (0.00024, -0.00024) (0.00023, -0.00023) (0.000225, -0.000225) (0.0002, -0.0002) (0.00035, -0.0002) (0.0004, -0.0002) (0.0006, -0.0002) (0.00018, -0.00018) (0.000175, -0.000175) (0.00017, -0.00017) (-0.0004, -0.00015) (0.00015, -0.00015) (0.0004, -0.00015) (0.00014, -0.00014) (0.000125, -0.000125) (0.00012, -0.00012) (0.000115, -0.000115) (0.00011, -0.00011) (0.0001, -0.0001) (0.00035, -0.0001) (0.0004, -0.0001) (0.0005, -0.0001) (0.0006, -0.0001) (9.0e-05, -9.0e-05) (4.0e-05, -8.0e-05) (8.0e-05, -8.0e-05) (0.0005, -8.0e-05) (0.0009, -8.0e-05) (7.5e-05, -7.5e-05) (7.0e-05, -7.0e-05) (0.00017, -7.0e-05) (6.0e-05, -6.0e-05) (-0.00015, -5.0e-05) (5.0e-05, -5.0e-05) (0.0001, -5.0e-05) (4.0e-05, -4.0e-05) (3.5e-05, -3.5e-05) (3.0e-05, -3.0e-05) (2.5e-05, -2.5e-05) (2.0e-05, -2.0e-05) (5.0e-05, -2.0e-05) (8.0e-05, -2.0e-05) (1.5e-05, -1.5e-05) (1.2e-05, -1.2e-05) (-8.0e-05, -1.0e-05) (1.0e-05, -1.0e-05) (8.0e-06, -8.0e-06) (5.0e-06, -5.0e-06) (4.0e-06, -4.0e-06) (3.0e-06, -3.0e-06) (2.5e-06, -2.5e-06) (2.0e-06, -2.0e-06) (1.0e-06, -1.0e-06) (2.0e-07, -2.0e-07) (-0.0018, 0.0) (-0.0015, 0.0) (-0.0013, 0.0) (-0.001, 0.0) (-0.00085, 0.0) (-0.0008, 0.0) (-0.0007, 0.0) (-0.0006, 0.0) (-0.0005, 0.0) (-0.00045, 0.0) (-0.0004, 0.0) (-0.00035, 0.0) (-0.00015, 0.0) (0.0, 0.0) (2.5e-05, 0.0) (6.0e-05, 0.0) (0.0001, 0.0) (0.00015, 0.0) (0.0002, 0.0) (0.00025, 0.0) (0.0003, 0.0) (0.00035, 0.0) (0.0004, 0.0) (0.00042, 0.0) (0.0005, 0.0) (0.0007, 0.0) (0.0008, 0.0) (5.0e-06, 5.0e-06) (1.0e-05, 1.0e-05) (2.5e-05, 2.5e-05) (5.0e-05, 5.0e-05) (0.0001, 0.0001) (0.00018, 0.0001) (0.00015, 0.00015) (0.00018, 0.00018) (0.0002, 0.0002) (0.00025, 0.00025) (0.0003, 0.0003) (0.00035, 0.00035) (450.0, 450.0)
Attribute: rated-temperature (min, max)
Values:
(-80.0, 280.0) (-65.0, 125.0) (-65.0, 150.0) (-65.0, 155.0) (-65.0, 165.0) (-65.0, 170.0) (-65.0, 175.0) (-65.0, 200.0) (-65.0, 225.0) (-65.0, 230.0) (-65.0, 250.0) (-65.0, 275.0) (-65.0, 350.0) (-60.0, 150.0) (-55.0, 105.0) (-55.0, 110.0) (-55.0, 125.0) (-55.0, 145.0) (-55.0, 150.0) (-55.0, 155.0) (-55.0, 170.0) (-55.0, 175.0) (-55.0, 180.0) (-55.0, 195.0) (-55.0, 200.0) (-55.0, 210.0) (-55.0, 215.0) (-55.0, 220.0) (-55.0, 225.0) (-55.0, 230.0) (-55.0, 235.0) (-55.0, 250.0) (-55.0, 270.0) (-55.0, 275.0) (-55.0, 300.0) (-55.0, 350.0) (-55.0, 355.0) (-50.0, 125.0) (-50.0, 150.0) (-40.0, 85.0) (-40.0, 110.0) (-40.0, 125.0) (-40.0, 130.0) (-40.0, 150.0) (-40.0, 155.0) (-40.0, 175.0) (-40.0, 200.0) (-40.0, 220.0) (-40.0, 275.0) (-25.0, 100.0) (-25.0, 125.0) (-25.0, 150.0) (-25.0, 155.0) (-20.0, 125.0) (-15.0, 105.0)
Attribute | Values |
---|
Attribute: mpn
Values:
More than 1000 values...
Attribute: dimensions (x, y, z, area)
Values:
More than 1000 values...
Attribute: stock
Values:
More than 1000 values...
Attribute: price
Values:
More than 1000 values...
Attribute: minimum_quantity
Values:
312 values
Attribute: metadata.datasheets
Values:
More than 1000 values...
Attribute: metadata.image
Values:
More than 1000 values...
Attribute: metadata.digi-key-part-number
Values:
More than 1000 values...
Attribute: metadata.description
Values:
More than 1000 values...
Attribute: metadata.series
Values:
More than 1000 values... |
Attribute: metadata.packaging
Values:
"Bag" "Box" "Bulk" "Cut Tape (CT)" "Digi-Reel®" "Strip" "Tape & Box (TB)" "Tape & Reel (TR)" "Tray" "Tube"
Attribute: metadata.features
Values:
"Anti-Arc, Current Sense, Flame Proof, Non-Inductive, Pulse Withstanding, Safety" "Anti-Arc, Flame Proof, Moisture Resistant, Non-Inductive, Safety" "Anti-Arc, Flame Proof, Moisture Resistant, Safety" "Anti-Corrosive, Automotive AEC-Q200, Moisture Resistant" "Anti-Corrosive, Current Sense, Moisture Resistant" "Anti-Corrosive, Flame Proof, Moisture Resistant, Safety" "Anti-Sulfur" "Anti-Sulfur, Automotive" "Anti-Sulfur, Automotive AEC-Q200" "Anti-Sulfur, Automotive AEC-Q200, Current Sense" "Anti-Sulfur, Automotive AEC-Q200, Current Sense, Moisture Resistant" "Anti-Sulfur, Automotive AEC-Q200, Current Sense, Moisture Resistant, Pulse Withstanding" "Anti-Sulfur, Automotive AEC-Q200, High Voltage" "Anti-Sulfur, Automotive AEC-Q200, High Voltage, Moisture Resistant" "Anti-Sulfur, Automotive AEC-Q200, Moisture Resistant" "Anti-Sulfur, Automotive AEC-Q200, Moisture Resistant, Pulse Withstanding" "Anti-Sulfur, Automotive AEC-Q200, Pulse Withstanding" "Anti-Sulfur, Current Sense" "Anti-Sulfur, Current Sense, Moisture Resistant, Pulse Withstanding" "Anti-Sulfur, Flame Proof, High Voltage, Pulse Withstanding, Safety" "Anti-Sulfur, Moisture Resistant" "Anti-Sulfur, Moisture Resistant, Non-Inductive" "Anti-Sulfur, Moisture Resistant, Non-Inductive, Non-Magnetic" "Automotive AEC-Q200" "Automotive AEC-Q200, Current Sense" "Automotive AEC-Q200, Current Sense, Moisture Resistant" "Automotive AEC-Q200, Current Sense, Moisture Resistant, Pulse Withstanding" "Automotive AEC-Q200, Current Sense, Pulse Withstanding" "Automotive AEC-Q200, Flame Retardant Coating, Safety" "Automotive AEC-Q200, High Voltage" "Automotive AEC-Q200, High Voltage, Moisture Resistant" "Automotive AEC-Q200, High Voltage, Pulse Withstanding" "Automotive AEC-Q200, Moisture Resistant" "Automotive AEC-Q200, Moisture Resistant, Non-Inductive" "Automotive AEC-Q200, Moisture Resistant, Pulse Withstanding" "Automotive AEC-Q200, Non-Inductive" "Automotive AEC-Q200, Pulse Withstanding" "Bonding Mountable, Moisture Resistant" "Bonding Mountable, Moisture Resistant, RF, High Frequency" "Current Sense" "Current Sense, Flame Proof, Moisture Resistant, Pulse Withstanding, Safety" "Current Sense, Flame Proof, Moisture Resistant, Safety" "Current Sense, Flame Proof, Non-Inductive, Pulse Withstanding, Safety" "Current Sense, Flame Proof, Pulse Withstanding, Safety" "Current Sense, Flame Proof, Safety" "Current Sense, Flame Retardant Coating, Safety" "Current Sense, Moisture Resistant" "Current Sense, Moisture Resistant, Non-Inductive" "Current Sense, Moisture Resistant, Non-Inductive, Non-Magnetic" "Current Sense, Moisture Resistant, Non-Inductive, Pulse Withstanding" "Current Sense, Moisture Resistant, Pulse Withstanding" "Current Sense, Non-Inductive" "Current Sense, Pulse Withstanding" "Flame Proof, Fusible, Moisture Resistant, Safety" "Flame Proof, Fusible, Pulse Withstanding, Safety" "Flame Proof, Fusible, Safety" "Flame Proof, High Voltage, Moisture Resistant, Non-Inductive, Safety" "Flame Proof, High Voltage, Non-Inductive, Safety" "Flame Proof, High Voltage, Pulse Withstanding, Safety" "Flame Proof, High Voltage, Safety" "Flame Proof, Moisture Resistant, Non-Inductive, Safety" "Flame Proof, Moisture Resistant, Non-Magnetic, Safety" "Flame Proof, Moisture Resistant, Pulse Withstanding, Safety" "Flame Proof, Moisture Resistant, Safety" "Flame Proof, Non-Inductive, Pulse Withstanding, Safety" "Flame Proof, Non-Inductive, Safety" "Flame Proof, Pulse Withstanding, Safety" "Flame Proof, Safety" "Flame Retardant Coating, Fusible, Pulse Withstanding, Safety" "Flame Retardant Coating, High Voltage, Moisture Resistant, Pulse Withstanding, Safety" "Flame Retardant Coating, High Voltage, Moisture Resistant, Safety" "Flame Retardant Coating, High Voltage, Pulse Withstanding, Safety" "Flame Retardant Coating, High Voltage, Safety" "Flame Retardant Coating, Military, Moisture Resistant, Safety" "Flame Retardant Coating, Moisture Resistant, Non-Inductive, Pulse Withstanding, Safety" "Flame Retardant Coating, Moisture Resistant, Safety" "Flame Retardant Coating, Non-Inductive, Safety" "Flame Retardant Coating, Pulse Withstanding, Safety" "Flame Retardant Coating, RF, High Frequency, Safety" "Flame Retardant Coating, Safety" "Fusible, Moisture Resistant, Safety" "Fusible, Pulse Withstanding, Safety" "Fusible, Safety" "High Voltage" "High Voltage, Moisture Resistant" "High Voltage, Moisture Resistant, Non-Inductive" "High Voltage, Moisture Resistant, Non-Magnetic" "High Voltage, Moisture Resistant, Pulse Withstanding" "High Voltage, Non-Inductive" "High Voltage, Non-Magnetic" "High Voltage, Pulse Withstanding" "Military" "Military, Moisture Resistant" "Military, Moisture Resistant, Non-Inductive" "Military, Moisture Resistant, Weldable" "Military, Non-Inductive" "Moisture Resistant" "Moisture Resistant, Non-Inductive" "Moisture Resistant, Non-Inductive, Non-Magnetic" "Moisture Resistant, Non-Inductive, Pulse Withstanding" "Moisture Resistant, Pulse Withstanding" "Non-Inductive" "Non-Inductive, Pulse Withstanding" "Non-Magnetic" "Pulse Withstanding" "RF, High Frequency" |
Attribute: metadata.supplier-device-package
Values:
102.0 201.0 204.0 207.0 306.0 402.0 404.0 406.0 505.0 508.0 510.0 603.0 606.0 612.0 705.0 805.0 808.0 815.0 816.0 830.0 1005.0 1010.0 1020.0 1050.0 1206.0 1210.0 1216.0 1218.0 1220.0 1224.0 1225.0 1313.0 1506.0 1575.0 1608.0 1632.0 1812.0 2010.0 2012.0 2015.0 2016.0 2208.0 2412.0 2512.0 2515.0 2520.0 2615.0 2725.0 2726.0 2728.0 2816.0 2817.0 2818.0 3014.0 3216.0 3264.0 3512.0 3637.0 3920.0 3921.0 4020.0 4026.0 4124.0 4318.0 4527.0 5020.0 5322.0 5931.0 6227.0 6432.0 6927.0 7638.0 8035.0 9045.0 11050.0 15075.0 "2-SMD" "Axial" "D-Pak in TO-263 Housing" "D2PAK" "DPAK" "D²PAK" "Flex-2" "MELF" "Mini MELF" "PFC10" "Panasert" "Powerchip®" "Radial" "Radial Lead" "SM-5" "SM2" "SMD" "SMD J-Lead, Pedestal" "SMD J-Lead, Recessed" "SR10" "SR20" "TO-126" "TO-126 (D-Pak Style)" "TO-218 (TO-247)" "TO-220" "TO-220-2" "TO-220-4" "TO-247" "TO-247-2" "TO-252" "TO-252, (D-Pak)" "TO-263" "TO-263 (D²Pak)"
Attribute: metadata.number-of-terminations
Values:
2.0 3.0 4.0
Capacitors
There are almost 1,000,000 capacitors in the JITX database but the same part can be referenced in different packaging (cut tape, reels...).
ceramic-cap
finds a specific capacitor based on specified properties and stock.
look-up-ceramic-caps
is a function that returns lists of capacitors based on properties and stock.
cap-strap
does everything ceramic-cap
does and connects it to two nets.
gen-cap-cmp
allows us to place generic capacitor components for offline use.
The db-parts section describes lower level functions, and the properties section describes every available attribute we can search with.
Contents:
- ocdb/utils/generic-components
- ocdb/utils/db-parts
- Properties
- Attribute:
manufacturer
- Attribute:
case
- Attribute:
rated-voltage
- Attribute:
rated-temperature (min, max)
- Attribute :
mpn
- Attribute :
capacitance
- Attribute :
dimensions
- Attribute :
mounting
- Attribute :
stock
- Attribute :
price
- Attribute :
minimum_quantity
- Attribute :
type
- Attribute :
anode
- Attribute :
electrolyte
- Attribute :
esr
- Attribute :
esr_frequency
- Attribute :
rated-voltage-ac
- Attribute :
rated-current-pk
- Attribute :
rated-current-rms
- Attribute :
temperature-coefficient.code
- Attribute :
temperature-coefficient.lower-temperature
- Attribute :
temperature-coefficient.raw_data
- Attribute :
temperature-coefficient.tolerance
- Attribute :
temperature-coefficient.upper-temperature
- Attribute :
temperature-coefficient.value
- Attribute :
tolerance
- Attribute :
temperature-coefficient.change (min, max)
- Attribute :
metadata.datasheets
- Attribute :
metadata.image
- Attribute :
metadata.digi-key-part-number
- Attribute :
metadata.description
- Attribute :
metadata.packaging
- Attribute :
metadata.ripple-current-low-frequency
- Attribute :
metadata.ripple-current-high-frequency
- Attribute:
metadata.lifetime-temp
- Attribute:
metadata.applications
- Attribute:
metadata.lead-spacing
- Attribute:
ocdb/utils/generic-components
The capacitor functions are located in ocdb/utils/generic-components
, which imports ocdb/utils/db-parts
.
ceramic-cap
We can query an actual SMD capacitor from the JITX database that can be bought. This call requires internet access.
ceramic-cap
calls the lower level Part Query API adding the list of attributes provided
by the user on top of design settings.
public defn ceramic-cap (params:Tuple<KeyValue>)
Here are the quick accessors:
public defn ceramic-cap (capacitance:Double, tolerance:Double)
public defn ceramic-cap (capacitance:Double)
The capacitance is in Farads and the tolerance is unit-less (0.01 is ±1%).
Example:
- c1 = 22pF ceramic capacitor
- c2 = 10nF±5% ceramic capacitor
- c3 = 1uF ceramic capacitor, 100V minimum rated voltage.
- c4 = 1uF±10% ceramic capacitor, 100V minimum rated voltage and a case of 1206.
- c5 = 1uF±10% ceramic capacitor, 100V minimum rated voltage and a case of 1206 or larger.
- c6 = 1uF ceramic capacitor, the negative tolerance between -5% and -22%. A tolerance of -20% +80% will satisfy this.
- c7 = 1uF ceramic capacitor, the negative tolerance between -5% and -22%, the positive tolerance between 60% and 100%. A tolerance of -20% +80% will satisfy this.
- c8 = 1uF, X7R ceramic capacitor.
- c9 = 68pF ceramic capacitor, 100V minimum rated voltage, type C0G
- c10 = 68pF ceramic capacitor, 100V minimum rated voltage, type X7R or C0G
- c11 = 1uF± ceramic capacitor, 60V minimum rated voltage, and
metadata.applications
matches one of the fields in theautomotive-metadata
value. - c12 = ceramic capacitor who's value is calculated based on a capacitance * a double, then rounded to the nearest 20% standard value, 60V minimum rated voltage.
- c13 = the largest value capacitor in case size 0805, 100V minimum rated voltage, and
metadata.applications
matches one of the fields in theautomotive-metadata
value.
#use-added-syntax(jitx)
defpackage my-design :
import ocdb/utils/generic-components
import ocdb/utils/design-vars
; Overrides default value set in ocdb/utils/design-vars
MIN-CAP-VOLTAGE = 6.3
pcb-module my-module :
inst c1 : ceramic-cap(22.0e-12)
inst c2 : ceramic-cap(10.0e-9, 0.05)
inst c3 : ceramic-cap(["capacitance" => 1.0e-6 "min-rated-voltage" => 100.0])
inst c4 : ceramic-cap(["capacitance" => 1.0e-6 "tolerance" => 0.10 "min-rated-voltage" => 100.0 "case" => "1206"])
inst c5 : ceramic-cap(["capacitance" => 1.0e-6 "tolerance" => 0.10 "min-rated-voltage" => 100.0 "case" => get-valid-pkg-list("1206")])
inst c6 : ceramic-cap(["capacitance" => 1.0e-6 "min-tolerance.min" => -0.22, "max-tolerance.min" => -0.05])
inst c7 : ceramic-cap(["capacitance" => 1.0e-6 "min-tolerance.min" => -0.22, "max-tolerance.min" => -0.05, "min-tolerance.max" => 0.6, "max-tolerance.max" => 1.0])
inst c8 : ceramic-cap(["capacitance" => 1.0e-6 "temperature-coefficient.code" => "X7R"])
inst c9 : ceramic-cap(["capacitance" => 68.0e-12 "min-rated-voltage" => 100.0 "temperature-coefficient.code" => "C0G"])
inst c10 : ceramic-cap(["capacitance" => 68.0e-12 "min-rated-voltage" => 100.0 "temperature-coefficient.code" => ["C0G" "X7R"]])
val automative-metadata = ["Audio, Automotive" "Automotive" "Automotive, Boardflex Sensitive" "Automotive, Boardflex Sensitive, ESD Protection" "Automotive, Bypass, Decoupling" "Automotive, Bypass, Decoupling, Boardflex Sensitive" "Automotive, Bypass, Decoupling, Boardflex Sensitive, ESD Protection" "Automotive, Bypass, Decoupling, ESD Protection" "Automotive, EMI, RFI Suppression" "Automotive, ESD Protection" "Automotive, High Temperature Reflow" "Automotive, SMPS Filtering" "Automotive, SMPS Filtering, Boardflex Sensitive" "Automotive, SMPS Filtering, Bypass, Decoupling" "Automotive; DC Link, DC Filtering" "Automotive; DC Link, DC Filtering; High Frequency, Switching; High Pulse, DV/DT" "Automotive; DC Link, DC Filtering; High Pulse, DV/DT; Snubber" "Automotive; EMI, RFI Suppression" "Automotive; High Frequency, Switching" "Automotive; High Frequency, Switching; High Pulse, DV/DT" "Automotive; High Frequency, Switching; High Pulse, DV/DT; Snubber" "Automotive; Power Factor Correction (PFC)" "High Reliability, Automotive" "High Reliability, Automotive, Boardflex Sensitive" "RF, Microwave, High Frequency, Automotive" "Safety, Automotive" "Safety, Automotive, Boardflex Sensitive"]
inst c11 : ceramic-cap(["capacitance" => 1.0e-6 "min-rated-voltage" => 60.0 "metadata.applications" => automative-metadata])
val test-voltage = 5.0
inst c12 : ceramic-cap(["capacitance" => closest-std-val(test-voltage * 1.0e-6,20.0) "min-rated-voltage" => 60.0])
inst c13 : ceramic-cap(["_sort" => ["-capacitance"] "case" => "0805" "min-rated-voltage" => 30.0 "metadata.applications" => automative-metadata])
Fixed Requirements :
- category: "capacitor"
- type: "ceramic"
- mounting: "smd"
- minimum_quantity: 1
Default design Requirements (set by global variables defined in ocdb/utils/design-vars
) :
- min-stock: 500
- _sort: ["area"]
- min-rated-voltage: 10.0
- max-rated-temperature.min: 0.0
- min-rated-temperature.max: 25.0
- case: ["0201" "02016" "0202" "0302" "0303" "0402" "0404" "0503" "0505" "0603" "0612" "0805" "1206" "1210" "1218" "1812" "2010" "2512" "2525" "2615" "2616" "3920" "4122" "4823" "5329" "6030"]
Those design requirements can be changed, either by changing ocdb/utils/design-vars
or by importing ocdb/utils/design-vars
and
setting new values for the global variables:
- min-stock: this is 5 times
DESIGN-QUANTITY
. Which means thatDESIGN-QUANTITY
default is 100. - _sort: set by
OPTIMIZE-FOR
. - min-rated-voltage: set by
MIN-CAP-VOLTAGE
in Volts. - [max-rated-temperature.min min-rated-temperature.max] : rated-temperature range in degC set by
OPERATING-TEMPERATURE
. - case: computed from
MIN-PKG
.MIN-PKG
default is "0201".
look-up-ceramic-caps
public defn look-up-ceramic-caps (attribute: String) -> Tuple
public defn look-up-ceramic-caps (attribute: String, filter-properties:Tuple<KeyValue>) -> Tuple
Looks up the list of available values (at most 1000 returned) for attribute
amongst chip capacitors in the JITX database.
This call filters on the same properties as ceramic-cap. Additional properties filter-properties
can
be given in argument to restrict further criteria on the considered chip capacitors.
Example:
$ jitx repl
stanza> import ocdb/utils/generic-components
stanza> println $ look-up-ceramic-caps("tolerance.max", ["capacitance" => 10.0e-9])
[0.01 0.02 0.05 0.1 0.2 0.8 1.0]
stanza> println $ look-up-ceramic-caps("rated-current-pk")
[]
stanza> println $ look-up-ceramic-caps("rated-voltage")
[10.0 16.0 25.0 35.0 50.0 63.0 75.0 80.0 100.0 150.0 200.0 250.0 450.0 500.0 630.0 1000.0 1200.0 1500.0 2000.0 2500.0 3000.0 4000.0 6000.0]
stanza> println $ look-up-ceramic-caps("rated-voltage", ["capacitance" => 50.0e-9, "tolerance" => 0.05])
[50.0 100.0]
In this example we can see that there rated current peak is not indicated for capacitors of type ceramic.
cap-strap
cap-strap
is a wrapper around ceramic-cap. So this call requires internet access.
It needs to be called inside a pcb module.
It instantiates a chip capacitor using provided params
in the current module, and
connects the pins of the ceramic capacitor to first-pin
and second-pin
that are JITXObjects from the module.
public defn cap-strap (first-pin:JITXObject, second-pin:JITXObject, params:Tuple<KeyValue>)
Quick accessors:
public defn cap-strap (first-pin:JITXObject, second-pin:JITXObject, capacitance:Double, tol:Double)
public defn cap-strap (first-pin:JITXObject, second-pin:JITXObject, capacitance:Double)
Example:
Instantiating a 10nF ceramic capacitor between the pins reset
and vio
#use-added-syntax(jitx)
defpackage my-design :
import ocdb/utils/generic-components
pcb-module my-module :
port reset
port vio
cap-strap(reset, vio, 10.0e-9)
gen-cap-cmp
public pcb-component gen-cap-cmp (cap:Double, tol:Double, max-v:Double, pkg-name:String)
This is a generic capacitor component, this is not an actual part that can be bought. This call does not require internet access.
Pins are p[1]
and p[2]
.
Arguments:
cap
: capacitance in Faradstol
: tolerance (1.0 means ±1%)max-v
: rated voltage in Voltspkg-name
: Package name (example: "0204")
Here are the quick accessors:
public defn gen-cap-cmp (cap:Double, max-v:Double)
public defn gen-cap-cmp (cap:Double, pkg:String)
public defn gen-cap-cmp (cap:Double)
Defaults used are:
tol
: 0.2%max-v
: 10.0 Vpkg-name
: "0402"
Example:
Instantiating a 5nF generic capacitor
pcb-module my-design :
inst r : gen-cap-cmp(5.0e-9)
ocdb/utils/db-parts
Capacitor Struct
Here is the Capacitor
struct. When capacitors are queried from the JITX database, the capacitor data gets populated into this struct.
It can be used to write pure stanza solvers without having to deal with jitx macros.
defstruct Capacitor <: Component :
; Generic properties
manufacturer: String
mpn: String
trust: String
x: Double with: (as-method => true)
y: Double with: (as-method => true)
z: Double|False
mounting: String
rated-temperature: MinMaxRange|False
case: String|False
sourcing: Sourcing
metadata: Tuple<KeyValue>
; Capacitor specific properties
type: String ; Type of resistor [“ceramic”, “film”, “electrolytic]
tolerance: MinMaxRange|False ; Guaranteed tolerance from manufacturer (Farad/Farad)
capacitance: Double ; Nominal capacitance (Farad)
anode: String|False ; Anode material of electrolytic capacitor [“aluminum”, “tantalum”, “niobium-oxide”]
electrolyte: String|False ; Electrolyte material of electrolytic capacitor [“polymer”, “manganese-dioxide”, “hybrid”, “non-solid”]
temperature-coefficient: String|False ; Temperature coefficient code of capacitance [“X7R”, ...]
esr: ESR|False
rated-voltage: Double|False ; Maximum voltage rating from manufacturer (Volts)
rated-voltage-ac: Double|False
rated-current-pk: Double|False ; Maximum peak current rating from manufacturer (Amperes)
rated-current-rms: Double|False ; Maximum rms current rating from manufacturer (Amperes)
sellers: Tuple<Seller>|False with: (as-method => true)
resolved-price: Double|False with: (as-method => true)
public defstruct MinMaxRange :
min: Double
max: Double
defstruct Sourcing :
price: Double|False
minimum-quantity: Int
stock: Int
defstruct ESR :
value: Double
frequency: Double|False
defmethod to-jitx (capacitor: Capacitor) -> Instantiable
Takes a Capacitor
struct and returns an instance.
Example:
#use-added-syntax(jitx)
defpackage my-design :
import ocdb/utils/db-parts
val capacitor = Capacitor(["tolerance" => 0.05 "min-rated-voltage" => 100.0])
pcb-module my-module :
inst cap : to-jitx(capacitor)
Capacitor Accessors
We can query an actual capacitor from the JITX database that we can be bought. This call requires internet access.
Capacitor
calls the lower level Part Query API adding the list of attributes provided
by the user on top of design settings.
public defn Capacitor (properties:Tuple<KeyValue>) -> Capacitor
Fixed Requirements :
- category: "capacitor"
Default design Requirements (set by global variables defined in ocdb/utils/design-vars
) :
- _sort: ["area"]
- max-rated-temperature.min: 0.0
- min-rated-temperature.max: 25.0
Those design requirements can be changed, either by changing ocdb/utils/design-vars
or by importing ocdb/utils/design-vars
and
setting new values for the global variables:
- _sort: set by
OPTIMIZE-FOR
. - [max-rated-temperature.min min-rated-temperature.max] : rated-temperature range in degC set by
OPERATING-TEMPERATURE
.
Here are accessors to override design requirements:
public defn Capacitor (properties:Tuple<KeyValue>,
exist:Tuple<String>) -> Capacitor
public defn Capacitor (properties:Tuple<KeyValue>,
exist:Tuple<String>,
sort:Tuple<String>,
operating-temperature:Toleranced|False) -> Capacitor
Arguments:
exist
: we can require the resulting capacitor to have an attribute that is otherwise optional (for example "tolerance").sort
: overrides the sorting arguments that would otherwise beOPTIMIZE-FOR
.operating-temperature
: overrides the rated temperature range that would otherwise beOPERATING-TEMPERATURE
.
public defn Capacitors (user-properties:Tuple<KeyValue>, limit: Int) -> Tuple<Capacitor>
Similar to Capacitor
but querying up to 25 capacitors.
public defn Capacitor (raw-json: JObject) -> Capacitor
Creates a Capacitor
from a JObject
.
Example:
$ jitx repl
stanza> import jitx/commands
stanza> import ocdb/utils/db-parts
stanza> import json
stanza> val jobject = dbquery-first(["category" => "capacitor" "capacitance" => 2.0e-6]) as JObject
stanza> println(jobject)
JObject(entries = ["_id" => "b733080d04beb31d7d340940" "trust" => "low" "category" => "capacitor" "mpn" => "BFC237934205" "mounting" => "through-hole" "manufacturer" => "Vishay Beyschlag/Draloric/BC Components" "type" => "film" "dimensions" => JObject(entries = ["x" => 31.5 "y" => 9.0 "z" => 19.0 "area" => 283.5]) "stock" => 0.0 "minimum_quantity" => 100.0 "metadata" => JObject(entries = ["datasheets" => "https://www.vishay.com/docs/28135/mkp379.pdf" "digi-key-part-number" => "BFC237934205-ND" "factory-stock" => 0.0 "qty" => 0.0 "packaging" => "Bulk" "series" => "MKP379" "dielectric-material" => "Polypropylene (PP), Metallized" "termination" => "PC Pins" "lead-spacing" => "1.083\" (27.50mm)" "applications" => "High Pulse, DV/DT" "features" => "Long Life"]) "price" => 7.5776 "tolerance" => JObject(entries = ["min" => -0.05 "max" => 0.05]) "capacitance" => 2.0e-06 "rated-voltage-ac" => 100.0 "rated-voltage" => 160.0 "rated-temperature" => JObject(entries = ["min" => -55.0 "max" => 85.0]) "case" => "Radial" "update_date" => "2021-09-04T01:26:09.754000"])
stanza> val capacitor = Capacitor(jobject)
stanza> println(capacitor)
Capacitor(
mpn = BFC237934205
trust = low
(x, y, z) = (31.5, 9.0, 19.0)
mounting = through-hole
rated-temperature = MinMaxRange(min=-55.0, max=85.0)
case = Radial
type = film
tolerance = MinMaxRange(min=-0.05, max=0.05)
capacitance = 2.0e-06
anode = false
electrolyte = false
temperature-coefficient = false
esr = false
rated-voltage = 160.0
rated-voltage-ac = 100.0
rated-current-pk = false
rated-current-rms = false
sourcing = ESR(price=7.5776, minimum-quantity=100, stock=0)
metadata =
"datasheets" => "https://www.vishay.com/docs/28135/mkp379.pdf"
"digi-key-part-number" => "BFC237934205-ND"
"factory-stock" => 0.0
"qty" => 0.0
"packaging" => "Bulk"
"series" => "MKP379"
"dielectric-material" => "Polypropylene (PP), Metallized"
"termination" => "PC Pins"
"lead-spacing" => "1.083\" (27.50mm)"
"applications" => "High Pulse, DV/DT"
"features" => "Long Life")
query-available-capacitance-values
public defn query-available-capacitance-values (properties:Tuple<KeyValue>, exist:Tuple<String>) -> Tuple<Double> :
We can query the list of available capacitance values available using the same design requirements as Capacitor
, filtering on a list of query parameters properties
and requiring the
attributes in exist
to exist on the capacitors.
Example:
$ jitx repl
stanza> import ocdb/utils/db-parts
println $ query-available-capacitance-values(["case" => "Axial" "min-rated-voltage" => 10.0e3], ["tolerance"])
[3.9e-10 4.7e-10 6.8e-10 1.0e-09 1.5e-09 1.8e-09 2.0e-09 2.2e-09 2.5e-09 2.7e-09 3.0e-09 3.3e-09 3.9e-09 4.7e-09 5.0e-09 6.8e-09 1.0e-08 4.7e-08]
look-up-capacitors
public defn look-up-capacitors (attribute: String) -> Tuple
public defn look-up-capacitors (attribute: String, filter-properties:Tuple<KeyValue>) -> Tuple
Looks up the list of available values (at most 1000 returned) for attribute
amongst capacitors in the JITX database.
This call filters on the same properties as Capacitor. Additional properties filter-properties
can
be given in argument to restrict further criteria on the considered capacitors.
Example:
$ jitx repl
stanza> import ocdb/utils/db-parts
stanza> println $ look-up-capacitors("mounting", ["capacitance" => 1.0e-6, "min-rated-voltage" => 1000.0])
["smd" "through-hole"]
Properties
Each capacitor has a different Digi-Key Part Number but an mpn can have up to 3 Digi-Key Part Numbers for 3 different packagings.
For example the capacitor of mpn "JMK021BJ103KK5W" appears with the following Digi-Key Part Numbers and packagings:
- 587-4850-1-ND: Cut Tape (CT)
- 587-4850-2-ND: Tape & Reel (TR)
This information can be found in the attributes metadata.digi-key-part-number
and metadata.packaging
but cannot be queried on.
We can check by ourselves doing:
$ jitx repl
stanza> import ocdb/utils/db-parts
stanza> do(println, Capacitors(["mpn" => "BFC237934205"], 25))
Check the properties reference for a description of supported attributes.
Here are available attribute values with default design requirements as of 10/14/2021. They can be queried anytime with:
$ jitx repl
stanza> import ocdb/utils/db-parts
stanza> for attribute in ["manufacturer", "mpn", "capacitance", "trust", "dimensions", "mounting", "case", "stock", "price", "minimum_quantity", "type", "anode", "electrolyte", "esr", "esr_frequency", "rated-voltage", "rated-voltage-ac", "rated-current-pk", "rated-current-rms", "temperature-coefficient.code", "temperature-coefficient.lower-temperature", "temperature-coefficient.raw_data", "temperature-coefficient.tolerance", "temperature-coefficient.upper-temperature", "temperature-coefficient.value", "metadata.datasheets", "metadata.image", "metadata.digi-key-part-number", "metadata.description", "metadata.packaging", "metadata.lifetime-temp", "metadata.applications", "metadata.ripple-current-low-frequency", "metadata.ripple-current-high-frequency", "metadata.lead-spacing"] do :
> val values = look-up-capacitors(attribute)
> if length(values) <= 200 :
> println("| %_ | %@ |" % [attribute, values])
> else :
> println("| %_ | %_ values |" % [attribute, length(values)])
stnaza> import json
stanza> for attribute in ["rated-temperature", "tolerance", "temperature-coefficient.change"] do :
> val values = to-tuple $ filter({_ is JObject}, look-up-capacitors(attribute))
> if length(values) <= 100 :
> println("| %_ (min, max) | %@ |" % [attribute, map({"(%_, %_)" % [_0["min"], _0["max"]]}, values)])
> else :
> println("| %_ | %_ values |" % [attribute, length(values)])
Attribute: manufacturer
Values:
"AHS Micro" "ASC Capacitors" "AVX Corporation" "Abracon LLC" "Aillen" "American Technical Ceramics" "CAL-CHIP ELECTRONICS, INC." "Cornell Dubilier Electronics (CDE)" "EPCOS - TDK Electronics" "EXXELIA" "EXXELIA Sic Safco" "Electronic Concepts Inc." "Elna America" "F&T" "Frontier Electronics" "Holy Stone Enterprise Co., Ltd." "IDT, Integrated Device Technology Inc" "Illinois Capacitor" "JJ Electronics" "Johanson Dielectrics Inc." "Johanson Technology Inc." "KEMET" "Knowles Dielectric Labs" "Knowles Novacap" "Knowles Syfer" "Kyocera International Inc. Electronic Components" "Meritek" "Murata Electronics" "NTE Electronics, Inc" "Nemco" "Nichicon" "Out of Bounds" "Paktron" "Panasonic Electronic Components" "PulseLarsen Antennas" "Rohm Semiconductor" "Rubycon" "SURGE" "Samsung Electro-Mechanics" "Semtech Corporation" "Songtian Electronics Co., Ltd" "Stackpole Electronics Inc" "TDK Corporation" "TE Connectivity Passive Product" "Taiyo Yuden" "Trigon Components" "Tusonix a Subsidiary of CTS Electronic Components" "United Chemi-Con" "Venkel" "Vicor Corporation" "Viking Tech" "Vishay Beyschlag/Draloric/BC Components" "Vishay Cera-Mite" "Vishay Dale" "Vishay Polytech" "Vishay Sprague" "Vishay Vitramon" "WIMA" "Walsin Technology Corporation" "Würth Elektronik" "XiangJiang" "Xicon" "YAGEO" "Yageo"
Attribute: case
Values:
"008004" "01005" "015008" "0201" "0202" "0204" "0301" "0303" "0306" "0402" "0502" "0504" "0505" "0508" "0602" "0603" "0612" "0709" "0803" "0805" "0905" "10-DIP" "10-SMD, Gull Wing" "1005" "1106" "1111" "12-DIP" "1206" "1210" "1218" "1305" "14-DIP" "14-SMD, Gull Wing" "1410" "1411" "1505" "1507" "1510" "16-DIP" "16-SMD, Gull Wing" "1611" "1706" "1708" "18-DIP" "18-SMD, Gull Wing" "1805" "1808" "1810" "1812" "1825" "1905" "1913" "2-DIP" "2-SMD" "2005" "2008" "2010" "2013" "2208" "2211" "2214" "2215" "2220" "2225" "2311" "2312" "2325" "2410" "2414" "2416" "2420" "2520" "2525" "2711" "2721" "2810" "2812" "2820" "2824" "2825" "2910" "2915" "2917" "2924" "3010" "3015" "3017" "3022" "3024" "3025" "3040" "3115" "3226" "3640" "3838" "3925" "3931" "40-DIP" "4030" "5040" "5829" "6-DIP" "6-SMD, Gull Wing" "6-Stacked SMD, J-Lead" "6-Stacked SMD, L-Lead" "6030" "6031" "6039" "6054" "6560" "8-DIP" "8-SMD, Gull Wing" "Axial" "Axial, Can" "Axial, Can - 4 Leads" "FlatPack" "FlatPack, Tabbed" "Nonstandard" "Nonstandard SMD" "Radial" "Radial - 12 Leads" "Radial - 3 Leads" "Radial - 4 Leads" "Radial - 5 Leads" "Radial - 6 Leads" "Radial, Can" "Radial, Can - 3 Lead" "Radial, Can - 4 Lead" "Radial, Can - 5 Lead" "Radial, Can - Mounting Ring - 4 Lead" "Radial, Can - QC Terminals" "Radial, Can - SMD" "Radial, Can - Snap-In" "Radial, Can - Snap-In - 3 Lead" "Radial, Can - Snap-In - 4 Lead" "Radial, Can - Snap-In - 5 Lead" "Radial, Can - Solder Lug" "Radial, Can - Solder Lug - 4 Lead" "Radial, Can - Solder Lug - 5 Lead" "Radial, Disc" "SMD, J-Lead" "Stacked DIP, 10 Lead" "Stacked DIP, 20 Lead" "Stacked DIP, 4 Lead" "Stacked DIP, 6 Lead" "Stacked DIP, 8 Lead" "Stacked SMD, 10 J-Lead" "Stacked SMD, 10 L-Lead" "Stacked SMD, 2 J-Lead" "Stacked SMD, 2 L-Lead" "Stacked SMD, 20 J-Lead" "Stacked SMD, 20 L-Lead" "Stacked SMD, 3 J-Lead" "Stacked SMD, 3 L-Lead" "Stacked SMD, 4 J-Lead" "Stacked SMD, 4 L-Lead" "Stacked SMD, 5 L-Lead" "Stacked SMD, 6 J-Lead" "Stacked SMD, 6 L-Lead" "Stacked SMD, 8 J-Lead"
Attribute: rated-voltage
Values:
2.0 2.5 3.0 4.0 5.0 6.0 6.3 7.0 7.5 8.0 8.2 10.0 12.0 12.5 15.0 16.0 20.0 21.0 25.0 30.0 32.0 35.0 40.0 42.0 50.0 55.0 56.0 60.0 63.0 70.0 71.0 75.0 80.0 100.0 125.0 150.0 160.0 180.0 200.0 201.0 210.0 220.0 225.0 230.0 250.0 251.0 275.0 280.0 300.0 305.0 315.0 330.0 350.0 360.0 385.0 400.0 420.0 440.0 450.0 475.0 500.0 520.0 525.0 550.0 560.0 575.0 580.0 600.0 630.0 650.0 700.0 720.0 750.0 760.0 800.0 840.0 850.0 875.0 900.0 920.0 1000.0 1100.0 1200.0 1250.0 1300.0 1400.0 1500.0 1600.0 1700.0 1800.0 2000.0 2500.0 3000.0 3150.0 3500.0 3600.0 4000.0 5000.0 6000.0 7200.0 7500.0 8000.0 9000.0 10000.0 15000.0 20000.0 25000.0 30000.0
Attribute: rated-temperature (min, max)
Values:
(-55.0, 85.0) (-55.0, 90.0) (-55.0, 100.0) (-55.0, 105.0) (-55.0, 110.0) (-55.0, 125.0) (-55.0, 135.0) (-55.0, 140.0) (-55.0, 145.0) (-55.0, 150.0) (-55.0, 160.0) (-55.0, 175.0) (-55.0, 200.0) (-55.0, 230.0) (-55.0, 250.0) (-55.0, 260.0) (-50.0, 105.0) (-45.0, 85.0) (-40.0, 60.0) (-40.0, 85.0) (-40.0, 100.0) (-40.0, 105.0) (-40.0, 110.0) (-40.0, 115.0) (-40.0, 125.0) (-40.0, 130.0) (-40.0, 135.0) (-40.0, 145.0) (-40.0, 150.0) (-40.0, 175.0) (-30.0, 85.0) (-30.0, 105.0) (-30.0, 125.0) (-25.0, 60.0) (-25.0, 70.0) (-25.0, 85.0) (-25.0, 105.0) (-25.0, 125.0) (-25.0, 130.0) (-25.0, 150.0) (-20.0, 85.0) (-10.0, 85.0)
Attribute :mpn
Values:
More than 1000 values...
Attribute :capacitance
Values:
705 values
Attribute :dimensions
Values:
More than 1000 values...
Attribute :mounting
Values:
"smd" "through-hole"
Attribute :stock
Values:
More than 1000 values...
Attribute :price
Values:
More than 1000 values...
Attribute :minimum_quantity
Values:
More than 712 values...
Attribute :type
Values:
"Conformal Coated" "Hermetically Sealed" "Hybrid" "Molded" "Polymer" "ceramic" "electrolytic" "film"
Attribute :anode
Values:
"aluminium" "aluminum" "tantalum"
Attribute :electrolyte
Values:
"hybrid" "manganese-dioxide" "non-solid" "polymer"
Attribute :esr
Values:
More than 1000 values...
Attribute :esr_frequency
Values:
20.0 100.0 120.0 1000.0 10000.0 20000.0 100000.0 300000.0 400000.0
Attribute :rated-voltage-ac
Values:
8.0 10.0 11.0 12.0 16.0 25.0 30.0 32.0 35.0 40.0 45.0 50.0 60.0 63.0 65.0 70.0 75.0 80.0 84.0 90.0 100.0 105.0 110.0 120.0 125.0 140.0 141.0 150.0 155.0 157.0 160.0 175.0 180.0 200.0 220.0 223.0 230.0 240.0 250.0 253.0 275.0 277.0 280.0 283.0 285.0 300.0 305.0 310.0 315.0 330.0 350.0 354.0 360.0 375.0 380.0 400.0 420.0 424.0 425.0 430.0 440.0 450.0 460.0 475.0 480.0 500.0 520.0 525.0 530.0 550.0 575.0 600.0 630.0 650.0 660.0 700.0 725.0 750.0 760.0 800.0 850.0 900.0 1000.0 1060.0 1500.0
Attribute :rated-current-pk
Values:
No value
Attribute :rated-current-rms
Values:
No value
Attribute :temperature-coefficient.code
Values:
"A" "B" "BD" "BG" "BJ" "BL" "BN" "BP" "BR" "BV" "BX" "C" "C0G" "C0H" "C0J" "C0K" "CCG" "CD" "CF" "CH" "CL" "E" "F" "GBBL" "JB" "L" "M" "N" "N1500" "N2000" "N2200" "N2500" "N2800" "N4700" "N750" "NP0" "NS" "P3K" "P90" "R" "R3L" "S3N" "SL" "SL/GP" "T" "U2J" "U2K" "U2M" "UNJ" "UX" "X0U" "X5F" "X5R" "X5S" "X5U" "X6S" "X6T" "X7R" "X7S" "X7T" "X7U" "X8G" "X8L" "X8M" "X8R" "XAN" "Y" "Y5F" "Y5P" "Y5R" "Y5S" "Y5T" "Y5U" "Y5V" "Y6P" "YSP" "Z5U" "Z5V" "ZLM"
Attribute :temperature-coefficient.lower-temperature
Values:
-55.0 -30.0 10.0
Attribute :temperature-coefficient.raw_data
Values:
"A" "B" "BD" "BG" "BJ" "BL" "BN" "BP" "BR" "BV" "BX" "C" "C0G, NP0" "C0G, NP0 (1B)" "C0H" "C0J" "C0K" "CCG" "CD" "CF" "CH" "CL" "E" "F" "GBBL" "JB" "L" "M" "N" "N1500" "N2000" "N2200" "N2500" "N2800" "N4700" "N750" "NP0" "NS" "P3K" "P90" "R" "R3L" "S3N" "SL" "SL/GP" "T" "U2J" "U2K" "U2M" "UNJ" "UX" "X0U" "X5F" "X5R" "X5S" "X5U" "X6S" "X6T" "X7R" "X7R (2R1)" "X7R (VHT)" "X7S" "X7T" "X7U" "X8G" "X8L" "X8M" "X8R" "XAN" "Y" "Y5F" "Y5P" "Y5P (B)" "Y5R" "Y5S" "Y5T" "Y5U" "Y5U (E)" "Y5V (F)" "Y6P" "YSP" "Z5U" "Z5V" "ZLM"
Attribute :temperature-coefficient.tolerance
Values:
3.0e-05 6.0e-05 0.00012 0.00025 0.0005 0.001 0.0025
Attribute :temperature-coefficient.upper-temperature
Values:
85.0 105.0 125.0 150.0
Attribute :temperature-coefficient.value
Values:
-0.0033 -0.0022 -0.0015 -0.00075 -0.0
Attribute :tolerance
Values:
355 values
Attribute :temperature-coefficient.change (min, max)
Values:
(-82.0, 22.0) (-56.0, 22.0) (-33.0, 22.0) (-22.0, 22.0) (-15.0, 15.0) (-10.0, 10.0)
Attribute :metadata.datasheets
Values:
More than 1000 values...
Attribute :metadata.image
Values:
More than 1000 values...
Attribute :metadata.digi-key-part-number
Values:
More than 1000 values...
Attribute :metadata.description
Values:
No value
Attribute :metadata.packaging
Values:
"Bag" "Box" "Bulk" "Cut Tape (CT)" "Digi-Reel®" "Strip" "Tape & Box (TB)" "Tape & Reel (TR)" "Tray" "Tube"
Attribute :metadata.ripple-current-low-frequency
Values:
More than 1000 values...
Attribute :metadata.ripple-current-high-frequency
Values:
More than 1000 values...
Attribute: metadata.lifetime-temp
Values:
"1000 Hrs @ 105°C" "1000 Hrs @ 125°C" "1000 Hrs @ 130°C" "1000 Hrs @ 135°C" "1000 Hrs @ 150°C" "1000 Hrs @ 200°C" "1000 Hrs @ 70°C" "1000 Hrs @ 85°C" "10000 Hrs @ 105°C" "10000 Hrs @ 125°C" "10000 Hrs @ 85°C" "100000 Hrs @ 60°C" "12000 Hrs @ 105°C" "12000 Hrs @ 85°C" "1250 Hrs @ 150°C" "13000 Hrs @ 85°C" "1500 Hrs @ 105°C" "1500 Hrs @ 125°C" "1500 Hrs @ 150°C" "1500 Hrs @ 85°C" "15000 Hrs @ 105°C" "15000 Hrs @ 85°C" "1600 Hrs @ 150°C" "18000 Hrs @ 85°C" "2000 Hrs @ 105°C" "2000 Hrs @ 125°C" "2000 Hrs @ 130°C" "2000 Hrs @ 135°C" "2000 Hrs @ 145°C" "2000 Hrs @ 150°C" "2000 Hrs @ 175°C" "2000 Hrs @ 200°C" "2000 Hrs @ 85°C" "20000 Hrs @ 105°C" "20000 Hrs @ 85°C" "22000 Hrs @ 105°C" "2500 Hrs @ 105°C" "2500 Hrs @ 125°C" "2500 Hrs @ 85°C" "26000 Hrs @ 85°C" "300 Hrs @ 200°C" "3000 Hrs @ 105°C" "3000 Hrs @ 125°C" "3000 Hrs @ 130°C" "3000 Hrs @ 135°C" "3000 Hrs @ 85°C" "3500 Hrs @ 125°C" "3500 Hrs @ 85°C" "37000 Hrs @ 105°C" "4000 Hrs @ 105°C" "4000 Hrs @ 125°C" "4000 Hrs @ 130°C" "4000 Hrs @ 135°C" "4000 Hrs @ 145°C" "4000 Hrs @ 85°C" "4600 Hrs @ 105°C" "500 Hrs @ 200°C" "500 Hrs @ 85°C" "5000 Hrs @ 105°C" "5000 Hrs @ 125°C" "5000 Hrs @ 85°C" "6000 Hrs @ 105°C" "6000 Hrs @ 125°C" "6300 Hrs @ 125°C" "7000 Hrs @ 105°C" "7000 Hrs @ 125°C" "8000 Hrs @ 105°C" "8000 Hrs @ 125°C" "8000 Hrs @ 85°C" "9000 Hrs @ 105°C"
Attribute: metadata.applications
Values:
"Acoustic Noise Reduction" "Audio" "Audio, Automotive" "Audio; DC Link, DC Filtering" "Audio; High Frequency, Switching; High Pulse, DV/DT" "Audio; High Frequency, Switching; High Pulse, DV/DT; Snubber" "Audio; High Pulse, DV/DT" "Automotive" "Automotive, Boardflex Sensitive" "Automotive, Boardflex Sensitive, ESD Protection" "Automotive, Bypass, Decoupling" "Automotive, Bypass, Decoupling, Boardflex Sensitive" "Automotive, Bypass, Decoupling, Boardflex Sensitive, ESD Protection" "Automotive, Bypass, Decoupling, ESD Protection" "Automotive, EMI, RFI Suppression" "Automotive, ESD Protection" "Automotive, High Temperature Reflow" "Automotive, SMPS Filtering" "Automotive, SMPS Filtering, Boardflex Sensitive" "Automotive, SMPS Filtering, Bypass, Decoupling" "Automotive; DC Link, DC Filtering" "Automotive; DC Link, DC Filtering; High Frequency, Switching; High Pulse, DV/DT" "Automotive; DC Link, DC Filtering; High Pulse, DV/DT; Snubber" "Automotive; EMI, RFI Suppression" "Automotive; High Frequency, Switching" "Automotive; High Frequency, Switching; High Pulse, DV/DT" "Automotive; High Frequency, Switching; High Pulse, DV/DT; Snubber" "Automotive; Power Factor Correction (PFC)" "Boardflex Sensitive" "Boardflex Sensitive, ESD Protection" "Bypass, Decoupling" "Bypass, Decoupling, Boardflex Sensitive" "Bypass, Decoupling, Boardflex Sensitive, ESD Protection" "Commutation; High Pulse, DV/DT; Snubber" "DC Link, DC Filtering" "DC Link, DC Filtering; EMI, RFI Suppression; High Pulse, DV/DT" "DC Link, DC Filtering; High Frequency, Switching" "DC Link, DC Filtering; High Frequency, Switching; High Pulse, DV/DT" "DC Link, DC Filtering; High Frequency, Switching; Snubber" "DC Link, DC Filtering; High Pulse, DV/DT" "DC Link, DC Filtering; High Pulse, DV/DT; Snubber" "DC Link, DC Filtering; Snubber" "Decoupling" "Downhole" "EMI, RFI Suppression" "EMI, RFI Suppression; 3 Phase" "EMI, RFI Suppression; High Frequency, Switching" "EMI, RFI Suppression; High Frequency, Switching; High Pulse, DV/DT" "EMI, RFI Suppression; High Pulse, DV/DT" "ESD Protection" "General Purpose" "Hermetically Sealed" "High Frequency, Switching" "High Frequency, Switching; High Pulse, DV/DT" "High Frequency, Switching; High Pulse, DV/DT; EMI, RFI Suppression" "High Frequency, Switching; High Pulse, DV/DT; Power Factor Correction (PFC)" "High Frequency, Switching; High Pulse, DV/DT; Snubber" "High Frequency; High Pulse, DV/DT; Power Factor Correction (PFC)" "High Pulse, DV/DT" "High Pulse, DV/DT; Snubber" "High Reliability" "High Reliability, Automotive" "High Reliability, Automotive, Boardflex Sensitive" "High Reliability, Boardflex Sensitive" "High Reliability, Bypass, Decoupling" "High Temperature Reflow" "Medical, Non-Critical" "Motor Run" "Motor Start" "Power Factor Correction (PFC)" "RF, Microwave, High Frequency" "RF, Microwave, High Frequency, Automotive" "RF, Microwave, High Frequency, Boardflex Sensitive" "RF, Microwave, High Frequency, Bypass, Decoupling" "SMPS Filtering" "SMPS Filtering, Boardflex Sensitive" "SMPS Filtering, Bypass, Decoupling" "Safety" "Safety, Automotive" "Safety, Automotive, Boardflex Sensitive" "Safety, Boardflex Sensitive" "Snubber"
Attribute: metadata.lead-spacing
Values:
"0.013" (0.33mm)" "0.039" (1.00mm)" "0.059" (1.50mm)" "0.079" (2.00mm)" "0.098" (2.50mm)" "0.100" (2.54mm)" "0.125" (3.18mm)" "0.138" (3.50mm)" "0.150" (3.80mm)" "0.150" (3.81mm)" "0.170" (4.32mm)" "0.180" (4.57mm)" "0.197" (5.00mm)" "0.200" (5.08mm)" "0.201" (5.10mm)" "0.220" (5.59mm)" "0.236" (6.00mm)" "0.246" (6.25mm)" "0.250" (6.35mm)" "0.252" (6.40mm)" "0.275" (6.98mm)" "0.275" (6.99mm)" "0.276" (7.00mm)" "0.295" (7.50mm)" "0.299" (7.60mm)" "0.300" (7.62mm)" "0.315" (8.00mm)" "0.325" (8.25mm)" "0.330" (8.38mm)" "0.331" (8.40mm)" "0.335" (8.50mm)" "0.354" (9.00mm)" "0.364" (9.25mm)" "0.374" (9.50mm)" "0.375" (9.52mm)" "0.375" (9.53mm)" "0.380" (9.65mm)" "0.394" (10.00mm)" "0.400" (10.15mm)" "0.400" (10.16mm)" "0.402" (10.20mm)" "0.413" (10.50mm)" "0.421" (10.70mm)" "0.423" (10.75mm)" "0.450" (11.43mm)" "0.475" (12.06mm)" "0.476" (12.10mm)" "0.480" (12.20mm)" "0.492" (12.50mm)" "0.500" (12.70mm)" "0.531" (13.50mm)" "0.543" (13.80mm)" "0.544" (13.84mm)" "0.551" (14.00mm)" "0.559" (14.20mm)" "0.563" (14.30mm)" "0.575" (14.60mm)" "0.580" (14.73mm)" "0.591" (15.00mm)" "0.598" (15.20mm)" "0.600" (15.24mm)" "0.602" (15.30mm)" "0.614" (15.60mm)" "0.630" (16.00mm)" "0.650" (16.50mm)" "0.657" (16.70mm)" "0.669" (17.00mm)" "0.673" (17.10mm)" "0.675" (17.14mm)" "0.681" (17.30mm)" "0.688" (17.48mm)" "0.689" (17.50mm)" "0.700" (17.78mm)" "0.709" (18.00mm)" "0.720" (18.30mm)" "0.728" (18.50mm)" "0.732" (18.60mm)" "0.752" (19.10mm)" "0.768" (19.50mm)" "0.780" (19.80mm)" "0.787" (20.00mm)" "0.791" (20.10mm)" "0.795" (20.20mm)" "0.799" (20.30mm)" "0.827" (21.00mm)" "0.846" (21.50mm)" "0.866" (22.00mm)" "0.886" (22.50mm)" "0.921" (23.40mm)" "0.965" (24.50mm)" "0.969" (24.60mm)" "0.975" (24.76mm)" "0.980" (24.90mm)" "0.984" (25.00mm)" "1.000" (25.40mm)" "1.031" (26.20mm)" "1.043" (26.50mm)" "1.063" (27.00mm)" "1.083" (27.50mm)" "1.091" (27.70mm)" "1.094" (27.79mm)" "1.094" (27.80mm)" "1.094" (28.80mm)" "1.098" (27.90mm)" "1.169" (29.70mm)" "1.175" (29.84mm)" "1.220" (31.00mm)" "1.240" (31.50mm)" "1.252" (31.80mm)" "1.280" (32.50mm)" "1.299" (33.00mm)" "1.339" (34.00mm)" "1.343" (34.10mm)" "1.346" (34.20mm)" "1.375" (34.92mm)" "1.378" (35.00mm)" "1.406" (35.70mm)" "1.476" (37.50mm)" "1.496" (38.00mm)" "1.575" (40.00mm)" "1.614" (41.00mm)" "1.654" (42.00mm)" "1.795" (45.60mm)" "1.831" (46.50mm)" "1.969" (50.00mm)" "2.067" (52.50mm)" "2.953" (75.00mm)"
Metadata values are not sanitized.
Inductor
There are about 100,000 inductors in the JITX database but the same part can be referenced in different packagings (cut tape, reels...).
Contents
ocdb/utils/generic-components
smd-inductor
We can query an actual SMD inductor from the JITX database that we can buy. This call requires internet access.
smd-inductor
calls the lower level Part Query API adding the list of attributes provided
by the user on top of design settings.
public defn smd-inductor (params:Tuple<KeyValue>)
Here are the quick accessors:
public defn smd-inductor (inductance:Double)
public defn smd-inductor (inductance:Double, tolerance:Double)
The inductance is in Henries and the tolerance is unit-less (0.01 is ±1%).
Example:
- inductor1 = 4.7µH±5% SMD inductor
- inductor2 = SMD inductor with the 20% standard value closest to 2µH, tolerance 10%, wirewound, 1210 size or larger, rated to 40° C or above
- inductor3 = inductor with value of 2µH or larger, saturation current of 200mA or larger, current rating of 1A or larger, 1210 size or larger, shielded or semi-shielded, rated to 40° C or above
#use-added-syntax(jitx)
defpackage my-design :
import ocdb/utils/generic-components
import ocdb/utils/design-vars
; Overrides default value set in ocdb/utils/design-vars
OPTIMIZE_FOR = ["cost"]
pcb-module my-module :
inst inductor1 : smd-inductor(4.7e-6, 0.05)
inst inductor2 : smd-inductor(["inductance" => closest-std-val(2.0e-6,20.0) "tolerance" => 0.10 "type" => "Wirewound" "case" => get-valid-pkg-list("1210") "min-rated-temperature.max" => 40.0])
inst inductor3 : smd-inductor(["min-inductance" => 2.0e-6 "min-saturation-current" => 0.2 "min-current-rating" => 1.0 "case" => get-valid-pkg-list("1210") "shielding" => ["shielded" "semi-shielded"] "min-rated-temperature.max" => 40.0])
Fixed Requirements :
- category: "inductor"
- mounting: "smd"
- minimum_quantity: 1
Default design Requirements (set by global variables defined in ocdb/utils/design-vars
) :
- min-stock: 500
- _sort: ["area"]
- max-rated-temperature.min: 0.0
- min-rated-temperature.max: 25.0
- case: ["0201" "02016" "0202" "0302" "0303" "0402" "0404" "0503" "0505" "0603" "0612" "0805" "1206" "1210" "1218" "1812" "2010" "2512" "2525" "2615" "2616" "3920" "4122" "4823" "5329" "6030"]
Those design requirements can be changed, either by changing ocdb/utils/design-vars
or by importing ocdb/utils/design-vars
and
setting new values for the global variables:
- min-stock: this is 5 times
DESIGN-QUANTITY
. Whisch means thatDESIGN-QUANTITY
default is 100. - _sort: set by
OPTIMIZE-FOR
. - [max-rated-temperature.min min-rated-temperature.max] : rated-temperature range in degC set by
OPERATING-TEMPERATURE
. - case: computed from
MIN-PKG
.MIN-PKG
default is "0201".
look-up-smd-inductors
public defn look-up-smd-inductors (attribute: String) -> Tuple
public defn look-up-smd-inductors (attribute: String, filter-properties:Tuple<KeyValue>) -> Tuple
Looks up the list of available values (at most 1000 returned) for attribute
amongst SMD inductors in the JITX database.
This call filters on the same properties as (smd-inductor)[#smd-inductor]. Additional properties filter-properties
can
be given in argument to restrict further criteria on the considered SMD inductors.
Example:
$ jitx repl
stanza> import ocdb/utils/generic-components
stanza> println $ look-up-smd-inductors("saturation-current", ["min-current-rating" => 0.1])
[0.05 0.055 0.06 0.07 0.08 0.085 0.09 0.1 0.11 0.115 0.12 0.125 0.13 0.135 0.14 0.145 0.15 0.16 0.17 0.18 0.19 0.2 0.205 0.21 0.22 0.23 0.24 0.25 0.27 0.275 0.28 0.29 0.3 0.32 0.33 0.34 0.35 0.36 0.366 0.37 0.38 0.39 0.4 0.42 0.425 0.44 0.45 0.47 0.48 0.5 0.515 0.52 0.53 0.55 0.6 0.614 0.62 0.65 0.68 0.7 0.72 0.73 0.75 0.772 0.78 0.8 0.85 0.89 0.9 0.95 0.98 1.0 1.02 1.05 1.06 1.1 1.131 1.15 1.2 1.24 1.25 1.3 1.35 1.4 1.45 1.5 1.6 1.65 1.7 1.71 1.75 1.8 1.85 2.0 2.05 2.1 2.15 2.2 2.26 2.3 2.4 2.5 2.6 2.75 2.8 2.9 3.0 3.1 3.3 3.5 3.6 3.8 4.0 4.1 4.2 4.3 4.4 4.5 4.6 4.7 4.8 4.9 5.0 5.2 5.4 6.3 6.5 7.0 7.5 8.5 10.0]
stanza> println $ look-up-smd-inductors("saturation-current", ["min-inductance" => 1.0e-6 "max-inductance" => 2.0e-6 "min-current-rating" => 0.1])
[0.08 0.1 0.13 0.14 0.16 0.18 0.19 0.2 0.22 0.25 0.28 0.29 0.3 0.36 0.4 0.45 0.5 0.55 0.65 0.7 0.8 0.85 0.89 0.9 1.0 1.1 1.15 1.2 1.35 1.4 1.5 1.8 1.85 2.0 2.1 2.2 2.26 2.4 2.6 2.9 3.1 3.8 4.0 4.3 4.4 4.6 5.2 6.3 7.5]
stanza> println $ look-up-smd-inductors("saturation-current", ["inductance" => 1.0e-6 "min-current-rating" => 0.1])
[0.1 0.14 0.19 0.2 0.22 0.28 0.29 0.36 0.4 0.5 0.65 0.7 0.8 0.85 0.89 0.9 1.0 1.1 1.15 1.35 1.4 1.5 2.0 2.1 2.26 2.4 2.9 3.1 3.8 4.3 4.4 4.6 5.2 6.3 7.5]
ind-strap
ind-strap
is a wrapper around smd-inductor. So this call requires internet access.
It needs to be called inside a pcb module.
It instantiates an SMD inductor using provided params
in the current module, and
connects the pins of the SMD inductor to first-pin
and second-pin
that are JITXObjects from the module.
public defn ind-strap (first-pin:JITXObject, second-pin:JITXObject, params:Tuple<KeyValue>)
Quick accessors:
public defn ind-strap (first-pin:JITXObject, second-pin:JITXObject, inductance:Double, tol:Double)
public defn ind-strap (first-pin:JITXObject, second-pin:JITXObject, inductance:Double)
Example:
Instantiating a 5µH SMD inductor between the pins reset
and vio
#use-added-syntax(jitx)
defpackage my-design :
import ocdb/utils/generic-components
pcb-module my-module :
port reset
port vio
ind-strap(reset, vio, 5.0e-6)
gen-ind-cmp
public pcb-component gen-ind-cmp (l-type:InductorSymbolType, ind:Double, tol:Double, max-i:Double)
This is a generic inductor component, this is not an actual part that we can buy. This call does not require internet access.
Pins are p[1]
and p[2]
.
Arguments:
l-type := InductorStd | InductorIronCore | InductorFerriteCore | InductorVariable | InductorPreset
from ocdb/utils/InductorSymbolType (decides the schematic symbol)ind
: inductance in Henriestol
: tolerance (1.0 means ±1%)max-i
: maximum current in amps
Here are the quick accessors:
public defn gen-ind-cmp (ind:Double, tol:Double) :
public defn gen-ind-cmp (ind:Double)
Defaults used are:
l-type
:InductorStd
tol
: 0.1%max-i
: 0.1 A
Example:
Instantiating a 5µH generic inductor
pcb-module my-design :
inst r : gen-ind-cmp(5.0e-6)
ocdb/utils/db-parts
Inductor Struct
Here is the Inductor
struct. When inductors are queried from the JITX database, the inductor data gets populated into this struct.
It can be used to write pure stanza solvers without having to deal with jitx macros.
defstruct Inductor <: Component :
; Generic properties
manufacturer: String
mpn: String
trust: String
x: Double with: (as-method => true)
y: Double with: (as-method => true)
z: Double|False
mounting: String
rated-temperature: MinMaxRange|False
case: String|False
sourcing: Sourcing
metadata: Tuple<KeyValue>
; Inductor specific properties
type: String ; Type of inductor ["Molded", "Multilayer", "Planar", "Thick Film", "Toroidal", "Wirewound", "adjustable", "fixed"]
tolerance: MinMaxRange|False ; Guaranteed tolerance from manufacture (Henry/Henry)
inductance: Double ; Nominal inductance (Henry)
material-core: String|False ; Composition of inductor [“ceramic”, “Ferrite”, ...]
shielding: String|False ; Magnetic field status [“semi-shielded”, “shielded”, “unshielded”]
current-rating: Double|False ; Maximum steady-state current rating from manufacture (Amperes)
saturation-current: Double|False ; Percentage inductance drop (typ 20-30%) at peak currents (Amperes)
dc-resistance: Double|False ; Nominal resistance (Ohm)
quality-factor: Double|False ; Loss factor inverse - ratio between inductors resistance and inductance (ratio@freq)
self-resonant-frequency: Double|False ; Frequency at which inductor impedance becomes very high / open circuit (freq in Hz)
sellers: Tuple<Seller>|False with: (as-method => true)
resolved-price: Double|False with: (as-method => true)
public defstruct MinMaxRange :
min: Double
max: Double
defstruct Sourcing :
price: Double|False
minimum-quantity: Int
stock: Int
defmethod to-jitx (inductor: Inductor) -> Instantiable
Takes a Inductor
struct and returns an instance.
Example:
#use-added-syntax(jitx)
defpackage my-design :
import ocdb/utils/db-parts
val inductor = Inductor(["tolerance" => 0.05 "type" => "Wirewound"])
pcb-module my-module :
inst ind : to-jitx(inductor)
Inductor Accessors
We can query an actual inductor from the JITX database that we can buy. This call requires internet access.
Inductor
calls the lower level Part Query API adding the list of attributes provided
by the user on top of design settings.
public defn Inductor (properties:Tuple<KeyValue>) -> Inductor
Fixed Requirements :
- category: "inductor"
Default design Requirements (set by global variables defined in ocdb/utils/design-vars
) :
- _sort: ["area"]
- max-rated-temperature.min: 0.0
- min-rated-temperature.max: 25.0
Those design requirements can be changed, either by changing ocdb/utils/design-vars
or by importing ocdb/utils/design-vars
and
setting new values for the global variables:
- _sort: set by
OPTIMIZE-FOR
. - [max-rated-temperature.min min-rated-temperature.max] : rated-temperature range in degC set by
OPERATING-TEMPERATURE
.
Here are accessors to override design requirements:
public defn Inductor (properties:Tuple<KeyValue>,
exist:Tuple<String>) -> Inductor
public defn Inductor (properties:Tuple<KeyValue>,
exist:Tuple<String>,
sort:Tuple<String>,
operating-temperature:[Double, Double]|False) -> Inductor
Arguments:
exist
: We can require the resulting inductor to have an attribute that is otherwise optional (for example "tolerance").sort
: overrides the sorting arguments that would otherwise beOPTIMIZE-FOR
.operating-temperature
: overrides the rated temperature range that would otherwise beOPERATING-TEMPERATURE
.
public defn Inductors (user-properties:Tuple<KeyValue>, limit: Int) -> Tuple<Inductor>
Similar to Inductor
but querying up to 25 inductors.
public defn Inductor (raw-json: JObject) -> Inductor
Creates a Inductor
from a JObject
.
Example:
$ jitx repl
stanza> import jitx/commands
stanza> import ocdb/utils/db-parts
stanza> import json
stanza> val jobject = dbquery-first(["category" => "inductor" "inductance" => 2.0e-6]) as JObject
stanza> println(jobject)
JObject(entries = ["_id" => "57079a8ac5097973f3964018" "trust" => "low" "category" => "inductor" "mpn" => "#A915AY-2R0M=P3" "mounting" => "smd" "manufacturer" => "Murata Electronics" "type" => "fixed" "dimensions" => JObject(entries = ["x" => 5.0 "y" => 5.0 "z" => 3.0 "area" => 25.0]) "stock" => 1308.0 "minimum_quantity" => 1.0 "metadata" => JObject(entries = ["datasheets" => "https://search.murata.co.jp/Ceramy/image/img/P02/J(E)TE243B-0046_D53LC_reference.pdf" "digi-key-part-number" => "490-#A915AY-2R0M=P3CT-ND" "description" => "FIXED IND 2UH 2.64A 27 MOHM SMD" "factory-stock" => 0.0 "qty" => 0.0 "packaging" => "Cut Tape (CT)" "series" => "D53LC" "inductance-frequency-test" => 100000.0]) "price" => 0.79 "tolerance" => JObject(entries = ["min" => -0.2 "max" => 0.2]) "inductance" => 2.0e-06 "current-rating" => 2.64 "saturation-current" => 2.92 "shielding" => "shielded" "dc-resistance" => 0.027 "case" => "Nonstandard" "update_date" => "2021-09-04T01:26:38.065000"])
stanza> val inductor = Inductor(jobject)
stanza> println(inductor)
Inductor(
mpn = #A915AY-2R0M=P3
trust = low
(x, y, z) = (5.0, 5.0, 3.0)
mounting = smd
rated-temperature = false
case = Nonstandard
type = fixed
tolerance = MinMaxRange(min=-0.2, max=0.2)
inductance = 2.0e-06
material-core = false
shielding = shielded
current-rating = 2.64
saturation-current = 2.92
dc-resistance = 0.027
quality-factor = false
self-resonant-frequency = false
sourcing = ESR(price=0.79, minimum-quantity=1, stock=1308)
metadata =
"datasheets" => "https://search.murata.co.jp/Ceramy/image/img/P02/J(E)TE243B-0046_D53LC_reference.pdf"
"digi-key-part-number" => "490-#A915AY-2R0M=P3CT-ND"
"description" => "FIXED IND 2UH 2.64A 27 MOHM SMD"
"factory-stock" => 0.0
"qty" => 0.0
"packaging" => "Cut Tape (CT)"
"series" => "D53LC"
"inductance-frequency-test" => 100000.0)
query-available-inductance-values
public defn query-available-inductance-values (properties:Tuple<KeyValue>, exist:Tuple<String>) -> Tuple<Double> :
We can query the list of available inductance values available using the same design requirements as Inductor
,
filtering on a list of query parameters properties
and requiring the attributes in exist
to exist on the indcutors.
Example:
$ jitx repl
stanza> import ocdb/utils/db-parts
stanza> println $ query-available-inductance-values(["min-current-rating" => 100.0], ["tolerance"])
[1.2e-07 4.7e-07 6.8e-07 8.2e-07 1.5e-06 2.2e-06 3.3e-06]
look-up-inductors
public defn look-up-inductors (attribute: String) -> Tuple
public defn look-up-inductors (attribute: String, filter-properties:Tuple<KeyValue>) -> Tuple
Looks up the list of available values (at most 1000 returned) for attribute
amongst inductors in the JITX database.
This call filters on the same properties as Inductor. Additional properties filter-properties
can
be given in argument to restrict further criteria on the considered inductors.
Example:
$ jitx repl
stanza> import ocdb/utils/db-parts
stanza> println $ look-up-inductors("saturation-current", ["inductance" => 1.0e-6 "case" => "0402"])
[0.1 1.0]
Properties
Each inductor has a different Digi-Key Part Number but an mpn has typically 3 Digi-Key Part Numbers for 3 different packagings.
For example the inductor of mpn "LQP02TN10NH02D" appears with the following Digi-Key Part Numbers and packagings:
- 490-14699-1-ND: Cut Tape (CT)
- 490-14699-6-ND: Digi-Reel®
- 490-14699-2-ND: Tape & Reel (TR)
This information can be found in the attributes metadata.digi-key-part-number
and metadata.packaging
but cannot be queried on.
We can check by ourselves doing:
$ jitx repl
stanza> import ocdb/utils/db-parts
stanza> do(println, Inductors(["mpn" => "LQP02TN10NH02D"], 25))
Check the properties reference for a description of supported attributes.
Here are available attribute values with default design requirements as of 10/14/2021. They can be queried anytime with:
$ jitx repl
stanza> import ocdb/utils/db-parts
stanza> for attribute in ["manufacturer", "mpn", "inductance", "trust", "dimensions", "mounting", "case", "stock", "price", "minimum_quantity", "type", "material-core", "shielding", "current-rating", "saturation-current", "dc-resistance", "quality-factor", "self-resonant-frequency", "metadata.datasheets", "metadata.image", "metadata.digi-key-part-number", "metadata.description", "metadata.packaging"] do :
> val values = look-up-inductors(attribute)
> if length(values) <= 250 :
> println("| %_ | %@ |" % [attribute, values])
> else :
> println("| %_ | %_ values |" % [attribute, length(values)])
stnaza> import json
stanza> for attribute in ["rated-temperature", "tolerance"] do :
> val values = to-tuple $ filter({_ is JObject}, look-up-resistors(attribute))
> if length(values) <= 100 :
> println("| %_ (min, max) | %@ |" % [attribute, map({"(%_, %_)" % [_0["min"], _0["max"]]}, values)])
> else :
> println("| %_ | %_ values |" % [attribute, length(values)])
Attribute | Values |
---|---|
manufacturer | "API Delevan Inc." "AVX Corporation" "Abracon LLC" "Allied Components International" "American Technical Ceramics" "Amgis, LLC" "BluaTec" "Bourns Inc." "COILCRAFT" "Chilisin Electronics" "Coilmx" "Delta Electronics/Components" "Delta Electronics/Cyntec" "ECS Inc." "EPCOS - TDK Electronics" "East Electronics" "Eaton - Electronics Division" "ITG Electronics, Inc." "Johanson Technology Inc." "KEMET" "Laird-Signal Integrity Products" "Littelfuse Inc." "Mag Layers" "Mentech Technology USA Inc." "Mini-Circuits" "Monolithic Power Systems Inc." "Murata Electronics" "Murata Power Solutions Inc." "Newava Technology Inc." "Panasonic Electronic Components" "Pulse Electronics Network" "Pulse Electronics Power" "Recom Power" "Samsung Electro-Mechanics" "Schaffner EMC Inc." "Schurter Inc." "Shenzhen Sunlord Electronics Co., Ltd." "Signal Transformer" "Standex-Meder Electronics" "Sumida America Components Inc." "TDK Corporation" "TE Connectivity" "TE Connectivity Passive Product" "Taiyo Yuden" "Talema Group LLC" "Traco Power" "Triad Magnetics" "Venkel" "Viking Tech" "Vishay Dale" "Vishay Electro-Films" "Walsin Technology Corporation" "Würth Elektronik" "XFMRS" "iNRCORE, LLC" |
mpn | More than 1000 values... |
inductance | 766 values |
trust | "low" |
dimensions | More than 1000 values... |
mounting | "smd" "through-hole" |
case | "01005" "0201" "0302" "0402" "0504" "0603" "0805" "0806" "1007" "1008" "11-DIP Module" "1206" "1207" "1210" "1212" "1812" "1919" "2-SMD" "2-SMD, J-Lead" "2005" "2020" "2220" "2304" "2323" "2512" "2727" "4-SMD" "8-DIP Module" "8-SMD, Gull Wing" "Axial" "Horizontal, 4 PC Pad" "Nonstandard" "Nonstandard, 2 Lead" "Nonstandard, 3 Lead" "Nonstandard, 4 Lead" "Nonstandard, Corner Terminals" "Radial" "Radial, Horizontal" "Radial, Horizontal - Corner Terminals" "Radial, Horizontal Cylinder" "Radial, Horizontal, 4 Leads" "Radial, Vertical" "Radial, Vertical - Corner Terminals" "Radial, Vertical Cylinder" "Radial, Vertical Cylinder, 3 Leads" "Radial, Vertical Cylinder, 4 Leads" "Radial, Vertical, 10 Leads" "Radial, Vertical, 4 Leads" "Radial, Vertical, 6 Leads" |
stock | More than 1000 values... |
price | More than 1000 values... |
minimum_quantity | 291 values |
type | "Ceramic" "Ceramic Core, Wirewound" "Molded" "Multilayer" "Planar" "Thick Film" "Thin Film" "Toroidal" "Wirewound" "fixed" |
material-core | "air" "alloy" "alloy-powder" "alumina" "carbonyl-powder" "ceramic" "ceramic-ferrite" "ceramic-non-magnetic" "ferrite" "iron" "iron-alloy" "iron-powder" "manganese-zinc-ferrite" "metal" "metal-composite" "molybdenum-permalloy" "nickel-zinc-ferrite" "non-magnetic" "phenolic" "sendust" |
shielding | "semi-shielded" "shielded" "unshielded" |
current-rating | More than 1000 values... |
saturation-current | More than 1000 values... |
dc-resistance | More than 1000 values... |
quality-factor | 0.3 0.5 0.64 0.7 0.8 0.85 0.9 1.0 1.1 1.2 1.3 1.4 1.6 1.8 2.0 2.3 2.6 2.8 2.9 3.0 3.1 3.2 3.3 3.4 3.5 3.8 3.9 4.0 4.2 5.0 5.8 6.0 6.3 6.7 6.8 7.0 7.2 7.3 7.4 7.5 7.6 7.7 7.72 7.9 8.0 8.12 8.2 8.24 8.26 8.3 8.42 8.5 8.58 8.6 8.64 8.7 8.78 8.8 8.9 9.0 9.1 9.18 9.2 9.24 9.34 9.4 9.5 9.56 9.624 9.66 9.7 9.74 9.8 9.9 10.0 10.2 10.5 10.6 10.76 10.88 10.9 11.0 11.26 11.3 11.64 11.72 11.8 12.0 12.7 12.9 13.0 13.1 13.18 13.26 13.32 13.6 13.86 14.0 14.7 15.0 15.5 16.0 16.46 16.48 17.0 17.46 18.0 18.5 19.0 20.0 21.0 22.0 23.0 24.0 25.0 26.0 27.0 28.0 29.0 30.0 31.0 32.0 33.0 34.0 35.0 36.0 37.0 38.0 39.0 40.0 41.0 42.0 43.0 44.0 45.0 46.0 47.0 48.0 49.0 50.0 51.0 52.0 53.0 54.0 55.0 56.0 57.0 58.0 59.0 60.0 61.0 62.0 63.0 64.0 65.0 66.0 67.0 68.0 69.0 70.0 71.0 72.0 73.0 74.0 75.0 76.0 77.0 78.0 79.0 80.0 81.0 82.0 84.0 85.0 86.0 87.0 88.0 89.0 90.0 92.0 93.0 94.0 95.0 96.0 97.0 98.0 100.0 102.0 104.0 105.0 106.0 107.0 109.0 110.0 112.0 115.0 120.0 126.0 127.0 128.0 130.0 131.0 132.0 133.0 135.0 137.0 139.0 140.0 142.0 143.0 145.0 150.0 151.0 155.0 163.0 184.0 186.0 191.0 211.0 223.0 226.0 230.0 240.0 245.0 280.0 |
self-resonant-frequency | More than 1000 values... |
rated-temperature (min, max) | (-80.0, 280.0) (-65.0, 125.0) (-65.0, 150.0) (-65.0, 155.0) (-65.0, 165.0) (-65.0, 170.0) (-65.0, 175.0) (-65.0, 200.0) (-65.0, 225.0) (-65.0, 230.0) (-65.0, 250.0) (-65.0, 275.0) (-65.0, 350.0) (-60.0, 150.0) (-55.0, 105.0) (-55.0, 110.0) (-55.0, 125.0) (-55.0, 145.0) (-55.0, 150.0) (-55.0, 155.0) (-55.0, 170.0) (-55.0, 175.0) (-55.0, 180.0) (-55.0, 195.0) (-55.0, 200.0) (-55.0, 210.0) (-55.0, 215.0) (-55.0, 220.0) (-55.0, 225.0) (-55.0, 230.0) (-55.0, 235.0) (-55.0, 250.0) (-55.0, 270.0) (-55.0, 275.0) (-55.0, 300.0) (-55.0, 350.0) (-55.0, 355.0) (-50.0, 125.0) (-50.0, 150.0) (-40.0, 85.0) (-40.0, 110.0) (-40.0, 125.0) (-40.0, 130.0) (-40.0, 150.0) (-40.0, 155.0) (-40.0, 175.0) (-40.0, 200.0) (-40.0, 220.0) (-40.0, 275.0) (-25.0, 100.0) (-25.0, 125.0) (-25.0, 150.0) (-25.0, 155.0) (-20.0, 125.0) (-15.0, 105.0) |
tolerance (min, max) | (-0.3, 0.0) (-0.3, 0.3) (-0.2, 0.2) (-0.15, 0.15) (-0.1, 0.0) (-0.1, 0.1) (-0.05, 0.05) (-0.03, 0.03) (-0.02, 0.02) (-0.01, 0.01) (-0.005, 0.005) (-0.0025, 0.0025) (-0.002, 0.002) (-0.001, 0.001) (-0.0005, 0.0005) (-0.0002, 0.0002) (-0.0001, 0.0001) (-5.0e-05, 5.0e-05) (-2.5e-05, 2.5e-05) (-2.0e-05, 2.0e-05) (-1.0e-05, 1.0e-05) |
metadata.datasheets | More than 1000 values... |
metadata.image | 1000 values |
metadata.digi-key-part-number | More than 1000 values... |
metadata.description | More than 1000 values... |
metadata.packaging | "Bag" "Box" "Bulk" "Cut Tape (CT)" "Digi-Reel®" "Strip" "Tape & Box (TB)" "Tape & Reel (TR)" "Tray" "Tube" |
Metadata values are not sanitized.
STM Micro-controllers
Here is the Quick Start Guide for the STM32 family: https://www.st.com/resource/en/application_note/dm00164549-getting-started-with-stm32f7-series-mcu-hardware-development-stmicroelectronics.pdf
Contents
ocdb/utils/micro-controllers
micro-controller
public defn micro-controller (params:Tuple<KeyValue>) -> (Tuple<KeyValue<Symbol,?>> -> Instantiable)
Calling micro-controller
will lookup a micro-controller from the JITX Parts Database. The component is wrapped inside a pcb-module
generator. micro-controller
calls the Part Query API with the list of attributes provided by the argument parameters and design variables. This function requires internet access to succeed.
Syntax
#use-added-syntax(jitx)
defpackage main :
import core
import jitx
import jitx/commands
; Required: import the micro-controllers package that defines the generator
import ocdb/utils/micro-controllers
pcb-module my-design :
; ...
; First, we specify the list of query parameters to query an MCU
val query-params = [
"core" => "ARM Cortex-M3"
]
; Next we look up the MCU generator using our query parameters.
val mcu-generator = micro-controller(query-params)
; Next, we specify the generator params. This tuple may be empty.
val generator-params = [
`cap-bypass-package => 4.7e-6
]
; Finally, we can call the generator and add it to our design.
inst mcu : generator(generator-params)
; ...
micro-controller(["core" => "ARM Cortex-M3"])
is a module generator for an STM micro-controller
based on the ARM Cortex-M3. It optionally can be run with argument [
`cap-bypass-package => 4.7e-6]
for our micro-controller
API to generate a module with the required configuration and requested capacitance for the package power supply bypass.
See the supported parameters for available query and generator parameters.
MicroController
Struct
public defstruct MicroController <: Component :
; Generic properties
manufacturer: String
mpn: String
trust: String
x: Double with: (as-method => true)
y: Double with: (as-method => true)
z: Double|False
mounting: String
rated-temperature: MinMaxRange|False
; Specific properties
core: String
core-architecture: String
data-width: Int
flash: Double
frequency: Double
io: Int
line: String
mfg-package: String
ram: Double
eeprom: Double
series: String
supply-voltage: MinMaxRange
rated-esd: Double|False
sellers: Tuple<Seller>|False with: (as-method => true)
resolved-price: Double|False with: (as-method => true)
pin-properties: STMPinProperties
bundles: Tuple<STMBundle>
supports: Tuple<STMSupports>
public defstruct MinMaxRange :
min: Double
max: Double
The MicroController
struct represents a micro-controller that can be converted into an instance or module. micro-controller
parts queries will populate the fields of this struct. Solvers may use this struct to access data without routing calls through the JITX language.
to-jitx
Takes a MicroController
struct and returns an Instantiable
.
defmethod to-jitx (m: MicroController) -> Instantiable
Syntax
#use-added-syntax(jitx)
defpackage my-design :
import core
import collections
import jitx
import jitx/commands
import ocdb/utils/db-parts
import ocdb/utils/micro-controllers
pcb-module my-module :
val mcu = MicroController(["max-io" => 40])
inst res : to-jitx(micro-controller)
MicroController
Query API
defn MicroController (parameters:Tuple<KeyValue>) -> MicroController
defn MicroController (parameters:Tuple<KeyValue>,
exist:Tuple<String>) -> MicroController
defn MicroController (parameters:Tuple<KeyValue>,
exist:Tuple<String>,
sort:Tuple<String>,
operating-temperature:[Double, Double]|False) -> MicroController
Create a Microcontroller
struct object from a set of query parameters using the Part Query API. This call requires internet access to reach the JITX parts database.
Arguments:
exist
: We can require the resulting micro-controller to have an attribute that is otherwise optional (for example "tolerance").sort
: overrides the sorting arguments that would otherwise beOPTIMIZE-FOR
.operating-temperature
: overrides the rated temperature range that would otherwise beOPERATING-TEMPERATURE
.
See the supported query parameters for available arguments to the parameters
field.
Syntax
Example: Find the best micro-controllers for our design requirements (optimizes by area by default, which is configured in `ocdb/utils/design-vars')
defpackage mcu-query :
import core
import collections
import jitx
import jitx/commands
import ocdb/utils/micro-controllers
val mcu = MicroController([])
println(mcu)
MicroController(
mpn = STM32L031G6U6
trust = low
(x, y, z) = (4.0, 4.0, 0.55)
mounting = smd
rated-temperature = MinMaxRange(min=-40.0, max=85.0)
core = ARM Cortex-M0+
core-architecture = ARM
data-width = 32
flash = 16000.0
frequency = 32000000.0
io = 21
line = STM32L0x1
mfg-package = UFQFPN28
ram = 8000.0
eeprom = 1024000.0
series = STM32L0
supply-voltage = MinMaxRange(min=-40.0, max=85.0)
rated-esd = 2000.0
sellers =
Seller(
company-name = Mouser
resolved-price = 2.55
offers = (
SellerOffer(
inventory-level = 110
prices = (
SellerOfferPrice(quantity = 1, converted-price = 3.29)
SellerOfferPrice(quantity = 10, converted-price = 2.96)
SellerOfferPrice(quantity = 50, converted-price = 2.96)
SellerOfferPrice(quantity = 100, converted-price = 2.55)
SellerOfferPrice(quantity = 250, converted-price = 2.4)
SellerOfferPrice(quantity = 500, converted-price = 2.11)
SellerOfferPrice(quantity = 1000, converted-price = 1.83)
SellerOfferPrice(quantity = 5880, converted-price = 1.82)
SellerOfferPrice(quantity = 10000, converted-price = 1.83)))))
resolved-price = 2.55)
public defn MicroControllers (properties:Tuple<KeyValue>, exist:Tuple<String>) -> Tuple<MicroController>
Similar to MicroController
but querying up to 25 micro-controllers (note the trailing s).
public defn MicroController (raw-json: JObject) -> MicroController
Creates a MicroController
from a JObject
.
Example:
defpackage query-mcu :
import core
import json
import jitx
import jitx/commands
import ocdb/utils/db-parts
import ocdb/utils/micro-controllers
val result = dbquery-first(["category" => "microcontroller"
"flash" => 32.0e3
"_stock" => 1,
"_sellers" => ["Mouser"] ])
val mcu = MicroController(result as JObject)
println(mcu)
MicroController(
mpn = STM32L010C6T6
trust = low
(x, y, z) = (9.0, 9.0, 1.6)
mounting = smd
rated-temperature = Toleranced(-40.0 <= 22.5 <= 85.0)
core = ARM Cortex-M0+
core-architecture = ARM
data-width = 32
flash = 32000.0
frequency = 32000000.0
io = 38
line = STM32L0x0
mfg-package = LQFP48
ram = 8000.0
eeprom = 256000.0
series = STM32L0
supply-voltage = MinMaxRange(min=1.8, max=3.6)
rated-esd = 2000.0
sellers =
Seller(
company-name = Mouser
resolved-price = 2.89
offers = (
SellerOffer(
inventory-level = 2
prices = (
SellerOfferPrice(quantity = 1, converted-price = 2.89)
SellerOfferPrice(quantity = 10, converted-price = 2.61)
SellerOfferPrice(quantity = 25, converted-price = 2.46)
SellerOfferPrice(quantity = 50, converted-price = 2.46)
SellerOfferPrice(quantity = 100, converted-price = 2.1)
SellerOfferPrice(quantity = 250, converted-price = 1.97)
SellerOfferPrice(quantity = 500, converted-price = 1.73)
SellerOfferPrice(quantity = 1000, converted-price = 1.43)
SellerOfferPrice(quantity = 3000, converted-price = 1.31)
SellerOfferPrice(quantity = 10000, converted-price = 1.31)))))
resolved-price = 2.89)
look-up-micro-controllers
public defn look-up-micro-controllers (attribute: String) -> Tuple
public defn look-up-micro-controllers (attribute: String, filter-properties:Tuple<KeyValue>) -> Tuple
Looks up the list of available query results for attribute
amongst micro-controllers in the JITX database. Results are capped at 1000.
This call filters on the same properties as MicroController. Additional properties filter-properties
can
be given in argument to restrict further criteria on the considered micro-controllers.
Syntax
defpackage lookup-micro-contollers :
import core
import jitx
import jitx/commands
import ocdb/utils/micro-controllers
; Query available cores in the JITX Parts Database
val available-cores = look-up-micro-controllers("core")
; Print them out
println("Available cores:")
for core in available-cores do :
println(" %_" % [core])
This function is useful for querying the currently available components in the JITX parts database.
ocdb/utils/stm-to-jitx
mcu-module
defn mcu-module (component:Instantiable) -> (Tuple<KeyValue<Symbol,?>> -> Instantiable) :
Create a microcontroller module generator from a component generated with to-jitx
from a MicroController
object.
Syntax
#use-added-syntax(jitx)
defpackage create-mcu-module :
import core
import collections
import jitx
import jitx/commands
import ocdb/utils/db-parts
import ocdb/utils/micro-controllers
import ocdb/utils/stm-to-jitx
pcb-module my-module :
; First create a MicroController object
val mcu-object = MicroController(["core" => "ARM Cortex-M3"])
; Then create an MCU instance
val mcu-instantiable = to-jitx(mcu-object)
; Now we can create an mcu-module generator
val mcu-module-generator = mcu-module(mcu-instantiable)
; And finally, create the module by calling the generator.
inst mcu : mcu-module-generator([])
micro-controller
is a wrapper around the logic above.
Supported Parameters
Supported Micro-controller Query Parameters
Parameter | Type | Description |
---|---|---|
"manufacturer" | String | The name of the manufacturer, for example `"STMicroelectronics" |
"mpn" | String | The MPN of the device. |
"line" | String | The product line of the device. |
"series" | String | The product series of the device. |
"core" | String | The core of the device, for example "ARM Cortex-M0" . |
"core-architecture" | String | The ISA of the device, for example "ARM" . |
"data-width" | Double | The data width of the device. |
"frequency" | Double | The core frequency of the device. |
"io" | Double | The number of GPIOs on the device. |
"flash" | Double | The amount of flash memory on the device. |
"ram" | Double | The amount of RAM on the device. |
"eeprom" | Double | The amount of EEPROM on the device. |
"rated-esd" | Double | The ESD rating of the device. |
"supply-voltage" | [Double, Double] | The [min, max] voltage supply requirements of the device. |
"mfg-package" | String | The package of the device. |
"rated-temperature" | [Double, Double] | The rated temperature of the device. Set by default with [../utils/design-vars.md]. |
"dimensions" | [Double, Double, Double, Double] | The [x, y, z, area] parameters of the device. |
"mounting" | String | The mounting of the component, for example "smd" or "th" |
Supported Micro-controller Module Generator Parameters
Option | Values | Default | Units | Description |
---|---|---|---|---|
`reset-pullup | Double | 10.0e3 | Ohms | The pullup resistor value on the reset pin and power. |
`reset-cap | Double | 10.0e-9 | Farads | The capacitor value between the reset pin and power. |
`p-bypass-package | Double | 4.7e-6 | Farads | The bypass capacitor value between the package power supply and ground. |
`p-bypass-pin | Double | 100.0e-9 | Farads | The bypass capacitor value between every power pin and ground. |
`boot-from | "flash" , "system" , "sram" | "flash" | The boot configuration option. | |
`boot-resistor | Double | 10.0e3 | Ohms | The boot pin resistor value. |
`debug-interface | Bundle | swd() | The desired debug interface. | |
`debug-connector | Instantiable | ocdb/components/tag-connect/TC2050-IDC-NL/module | The desired debug connector. | |
`HSE-freq | Double | 16.0e6 | Hz | High-frequency clock configuration |
`HSE-ppm | Double | 30.0e-6 | parts per million | |
`HSE-source | "crystal" , "osc" | "crystal" | ||
`LSE-freq | Double | 32.768e3 | Hz | Low-frequency clock configuration |
`LSE-ppm | Double | 0.05 | parts per million | |
`LSE-source | "crystal" , "osc" | "crystal" |
See the Parts Query API for a detailed reference on parts database queries.
Open Components Database
We have written a library of component models, reusable circuits, example designs, and utilities to make it easy to build new designs using JITX. We call this collection the open-components-database. You can use it as is, or use it as a jumping off point to write your own generators using JITX.
BOM Import Data Format
JITX supports importing BOM data for manufacturer names and manufacturer part numbers (MPN) in imported pcb-components
. This feature requires generating a jitx-import-bom.tsv
in the import data directory.
Prerequisites:
Preparing the Data for Import
BOM files must be tab-separated value (TSV) files named jitx-import-bom.tsv
. Each row must contain the following information :
Comma separated reference designators \t Manufacturer Name \t Input
Example TSV
C67, C75, C146 Samsung CL05B103KB5NNNC
C69, C176 Samsung CL21B475KOFNNNE
C71 TDK C1005X8R2A222K050BA
C72 Samsung CL21B102KCANNNC
C73, C201 TDK CKG57NX7S2A226M5
R11 Panasonic ERJ-2RKF5101X
R13, R17, R139 Yageo RC0402FR-07120RL
R19, R141 Susumu KRL6432E-M-R004-F-T
U1
U2 STMicroelectronics STM32F407ZGT6
U3, U4, U5, U6 NXP Freescale MPXV5010GP
U7
U8 TI Nat
Move the TSV File into place
The TSV file should be in the directory of the project to be imported.
Converting Number Types
How to convert to a Double?
Use the to-double(my-non-double-number)
to convert to a double.
How to convert to Toleranced?
Use one of the functions that makes a Toleranced
value to do this.
An easy solution is: typ(my-double)
. Other solutions can be found in the Toleranced documentation.
Schematic and Physical Design Reference
This reference page covers:
- How to use the interactive schematic to clean up, organize, and prepare your schematic.
- How to use the interactive physical design to layout and route your PCB.
NOTE: If you want a hands-on, illustrated, quickstart tutorial on how to use these interfaces, refer to Quickstart II: Organize and Layout a Design.
Schematic
After compiling a design with Ctrl+Enter
, you can open the schematic view at any time by clicking the "Schematic" button in the JITX VSCode panel.
Move a Symbol
Click and drag.
Hover your mouse over the symbol, click, hold the mouse button while dragging the symbol to where you want it to be, then let go of the mouse button.
Move Schematic Groups
Click and drag.
Hover your mouse over the edge/border of a schematic group, click, hold the mouse button while dragging the symbol to where you want it to be, the let go of the mouse button.
Move Schematic Group to New Sheet
Click, drag, and press number key.
Do the same action as "Move Schematic Groups", but while your mouse button is held down, press a number key from 1-9 to move the schematic group to the corresponding sheet.
Rotate a Symbol
Click and "r".
Hover your mouse over a component, click that component, then press the "r" key to rotate CCW. Press Shift+r
to rotate CW.
Invert/Mirror/Flip a Symbol
Click and "x" or "y".
Hover your mouse over a component, click that component, then press the "x" key to invert in X or "y" to invert in Y.
Explore Schematic Through Hierarchical Diagram
Press "0". Zoom in to hierarchical diagram. Click any box.
The first sheet in the sheets list is number 0. It contains a hierarchical diagram of your entire design. You can access this sheet by pressing "0" when in the schematic window. Zoom in to the diagram and click any box to open the sheet containing that subsystem and to auto-zoom to that submodule.
Select Net
Click wire and "a".
Click on any wire of a particular net, then press "a" to highlight all wires that correspond to that net.
Component Info Card
Hover and "e".
Hover over any component in the schematic and press "e" to reveal a sheet displaying information about that component.
Toggle Sheet List
"d"
The sheets list is the box at the bottom of the schematic UI that shows you a thumbnail of every sheet in your design. Press "d" to hide or unhide the sheets list.
Split Net
Click wire and "z".
Click any wire and press "z" to split up the net at that position. JITX schematics don't allow for the removal of nets, as connectivity is defined in code. If you want to change connectivity, you should go back to your code and do it there. Splitting nets is used for organization only, and doesn't remove connections.
Join Net
Click wire and "q".
Click any wire and press "q" to join up the net at that position. JITX schematics don't allow for the addition of nets, as connectivity is defined in code. If you want to change connectivity, you should go back to your code and do it there. Joining nets is used for organization of the schematic only, and doesn't create new connections.
View Hotkeys
Click "?" button in top right.
In the top right of the schematic view, there is a button that looks like a question mark ("?"). Click that to reveal all the latest hotkeys.
Undo / Redo
Ctrl+z
to undo, Ctrl+Shift+z
to redo.
You can undo previous actions at any point by pressing Ctrl+z
to rollback to a previous state. If you undo something that you decide you don't want to undo, press Ctrl+Shift+z
to redo the last undo.
Layout and Route
After compiling a design with Ctrl+Enter
, you can open the board view at any time by clicking the "Board" button in the JITX VSCode panel.
Place a Landpattern
Click to select, click, drag.
Hover your mouse over the landpattern and click it to select it. Once it's selected, click the landpattern, hold the mouse button while dragging the landpattern to where you want it to be, then let go of the mouse button. Note that landpatterns won't move if your mouse is hovering over a pad - make sure your house is within the landpattern bounding box, but not on top of a pad, in order to move components. Also note that landpatterns that were programmatically placed in code (using a place
in the top level module) can't be moved in the board view.
Rotate a Landpattern
Click and "r".
Hover your mouse over a landpattern, click that landpattern, then press the "r" key to rotate CCW. Press Shift+r
to rotate CW.
Flip a Landpattern
Click and "f".
Hover your mouse over a landpattern, click that landpattern, then press the "f" key to flip the landpattern to the other side of the board. Note you won't be able to select the landpattern after a flip until you've switched your focus to the oppositve layer (see "Use Different Layers" below).
Change Board Shape
Edit code.
The board shape is defined in your code and set with set-board
. Change the board shape parameters in code and reload the board view for your changes to take effect.
Route a Trace
Select multiple pads and vias, press "q".
Select multiple pads and/or vias by Shift+Click
or by clicking in empty space and dragging a selection box over multiple pads/vias (optionally holding Shift
during drag to add to selection). Then press "q" to autoroute between pads and vias on a single layer. Try to use the autoroute on a limited set of pads at a time for best results.
Route Pads that are Pin Assigned
Nets that are created using supports
/requires
in code are considered "pin assigned", and won't show a ratsnest in the board view. To route these nets, select that pads that are to be routed and press "q". Since the pin assignment doesn't happen until route time, there are multiple possible valid configurations that pin assigned nets can take on.
Place a Via
"v", then click and drag from pad.
Vias always belong to pads. To enter via mode, press "v". Then, hover over a pad, click and hold the mouse button, and drag your mouse out from the pad. Release the mouse button to place the via.
Click on pad, then Shift+V
.
Select a pad and press Shift+V
to automatically create a via for that pad.
Use Different Layers
PgUp/PgDown.
The layer where new routes appear depends on which layer is in focus. Use the PageUp
and PageDown
keys to between layers. Press "d" or click the layer box button in the top left of the board view (a button that looks like a stack of layers) to expose the layer view. Click the "eye" icon on any layer or sub-layer to hide it. Click the name of the layer to switch focus to that layer. Click the layer stack button again (or press "d") to hide the layer view.
Component Info Card
Hover and "e".
Hover over any landpattern in the schematic and press "e" to reveal a sheet displaying information about that component. Press "e" again to hide the component info card.
Save Layout
Ctrl+s
Press Ctrl+s
to save your current placement and layout. Or click the the "Save" button in the top left button bank to save.
Ratsnest Toggle
"o".
Press "o" anytime to toggle on/off the ratsnest.
Select All on Net
Click and "a".
To select all copper that belongs to net, select any aspect of that net (pad, trace, via, etc.) and press "a".
View Hotkeys
Click "?" button in top right.
In the top right of the board view, there is a button that looks like a question mark ("?"). Click that to reveal all the latest hotkeys.
Undo / Redo
Ctrl+z
to undo, Ctrl+Shift+z
to redo.
You can undo previous actions at any point by pressing Ctrl+z
to rollback to a previous state. If you undo something that you decide you don't want to undo, press Ctrl+Shift+z
to redo the last undo.
SLM - Stanza Library Manager
Stanza has a library manager called "Stanza Library Manager" or SLM for short. SLM is used to manage the dependencies for your JITX project. This include:
- The JITX Runtime and tracking compatibility as upgrades are made.
- The JITX standard libraries like OCDB
- Any third-party or user specific libraries that you choose to integrate into your project.
With SLM we can keep all of our code synchronized and use the latest features from the JITX environment.
How does SLM work?
SLM consists of two parts:
- The
slm
command-line executable that manages dependencies - The
slm.toml
configuration file that is present in any SLM capable projects.
Your interface to SLM will primarily be through the slm.toml
configuration file in your project. This file will
typically look like:
name = "motor-controller"
version = "0.1.0"
[dependencies]
JITX = { path = "{SLM_ROOT}/JITX" }
ocdb = { path = "{SLM_ROOT}/ocdb" }
Notice that these are the default dependencies that JITX adds by default. These are
path
dependencies and reference folders on your local computer's disk. Paths will use
forward slash on all platforms.
Adding Dependencies
The easiest way to add dependencies to your JITX project is by using the slm
command line
utility. In your JITX VSCode project, start by opening a terminal with Terminal -> New Terminal
.
In the created terminal, you can run:
$> $SLM add -git StanzaOrg/maybe-utils
$> $SLM clean
This would grab the latest tagged version of maybe-utils
from Github and add it to your local motor-controller/slm.toml
configuration file. This method requires git
on your $PATH
(or on Windows %PATH%
or in Powershell $env:PATH
).
The clean
command is a tool to tell slm
that the dependency expectations have changed.
IMPORTANT - After adding dependencies, it is a good idea to kill the JITX stanza terminal and re-run the
design with ctl-enter
. Otherwise, the new dependencies will not be downloaded.
How are dependencies added?
The previous add
command would result in the following slm.toml
file:
name = "motor-controller"
version = "0.1.0"
[dependencies]
JITX = { path = "{SLM_ROOT}/JITX" }
ocdb = { path = "{SLM_ROOT}/ocdb" }
maybe-utils = { git = "StanzaOrg/maybe-utils", version = "0.1.4" }
This will pull in the maybe-utils library and make it available for your project's code to use.
Ideally - you would never modify the slm.toml
file directly.
Adding Dependency By Path
You can alternatively add a dependency by path:
$> $SLM add -path ~/src/my-library
$> $SLM clean
This would add a my-library
dependency to the slm.toml
file that
references the ~/src/my-library
path directly. The my-library
directory must contain
a valid SLM project including a ~/src/my-library/slm.toml
file.
There are additional options to customize this behavior.
See $SLM -h
for more info.
IMPORTANT - After adding dependencies, it is a good idea to kill the JITX stanza terminal and re-run the
design with ctl-enter
. Otherwise, the new dependencies will not be downloaded.
Setting up Git
To add dependencies from Github, you will need ssh
and git
on your path. In
addition, you will need to have setup an SSH key
for your github account so that you can clone repositories.
To check that this is working correctly, run:
ssh -T git@github.com
Hi <user>! You've successfully authenticated, but GitHub does not provide shell access.
Setup HTTPS mode:
If all else fails and you , try setting the SLM_PROTOCOL
environment variable before launching VSCode:
$> cd motor-controller
$> export SLM_PROTOCOL="https"
$> code .
Tips for SLM on Windows
The environment variable syntax in powershell is a little different from Linux and Mac OS-X.
The SLM
environment variable is accessed with $env:SLM
. In addition, to launch $env:SLM
as an executable you will need to use a &
character prefix in powershell.
Example:
$> cd motor-controller
$> &$env:SLM add -git StanzaOrg/maybe-utils
$> &$env:SLM clean
Want to know more about SLM ?
Try running:
# Mac/Linux:
$SLM help
# Windows:
&$env:SLM help
Or review the code on Github: StanzaOrg/slm
Upgrading Legacy Projects
Projects created before JITX version 2.30 will likely have been created without the SLM configuration files. To upgrade your project and use SLM for managing dependencies moving forward, you will need to run a JITX command from the VSCode command palette. Here are the steps:
- Open the JITX project in VSCode that you wish to upgrade.
- Press
ctl + shift + P
to launch the command palette. - Type
JITX: Upgrade Legacy Project to SLM
and this should filter to a command option. - Press
ENTER
and the command will convert the projects present in the current workspace.
This new configuration will leave the code in your project as is, but it will update the dependencies. JITX-provided dependencies such as OCDB that your project probably uses will be upgraded to new versions.
This means that the open-components-database
directory will still exist but the SLM dependencies will no
longer reference it. Assuming you have not made any local changes to the open-components-database
directory in your project - it is safe to remove the open-components-database
folder.
If you are converting an older project, you may find your project won't compile anymore. You will likely need to make some updates to work with the latest version of OCDB. Don't hesitate to reach out if you run into trouble.
Known Issues
General
- CAD imports fail. Some Altium or KiCad designs may import into JITX but then fail to compile. We are solving this issue and all CAD projects will be importable in the coming weeks. If your design fails to compile after import, the first thing to do is to look at the output of the JITX Shell. It's likely the issue has to do with geometry of 1 or more of your components. Look for lines that begin with
Failed to initialize physical design state: Unrecoverable:
and note the component that is listed as the culprit. Then, go back to your original design and:- Ensure the landpattern for that component is correct. If you correct any issues, then retry.
- If the above fails, change the landpattern of the component to a dummy landpattern (example, a header pin or oscilloscope probe landpattern), re-import, and then generate the pattern directly in JITX.
- UI can be non-responsive when there is significant computation dedicated to the design automation.
- To see the status of the algorithms in VSCode, go to Output (bottom of VSCode window) and select
jitx-log
in the dropdown.
- To see the status of the algorithms in VSCode, go to Output (bottom of VSCode window) and select
- Board UI goes black. In this case, just close the window, navigate to the JITX sidebar, and click "Board".
Physical Design / Layout
- No ratnest is shown for pin-assigned nets. User must select pins that they know can be assigned to each other and click route. export-cad() can be used to make sure that everything has been assigned and routed.
- Vias cannot be moved once created. To deal with this, simply delete the via and create a new one in the new location.
- Not all possible pin assignments can be found by the router. Some pins that should be assignable will not be able to be assigned. Use nets, or route to a different pad.
- The router cannot route well around imported or user-defined traces yet. Some imported geometry from CAD can be malformed and fail the checks in the routing algorithms.
- Altium DRC errors occur after exporting from JITX.
- Ensure you're on the latest JITX VSCode extension version, then retry.
- Some errors related to disconnected vias and board edge clearance may still be thrown. These can be safely ignored. We're currently working to solve these errors.
Schematic
- Schematic changes may become slow in large schematic groups containing many symbols and pins. Create smaller groups to improve performance.
- Your schematic might initially look like a blank screen like below. Be sure to zoom in to the bottom left corner to see the design.
Altium Importer Errata
There are several known limitations/issues of Altium importer.
- Net lists may be modified because:
- Hidden pins connected to nets will be unhidden.
- Hidden pins will not be connected to nets.
- No-connect pins will not be imported.
- Net lists must be consistent across the schematic and layout. No net may have different pins on it in either the layout or schematic, and the same nets must exist in the layout and schematic.
- A schematic symbol with a pin not mapped to a landpattern pad will not be import