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 supports 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 nets

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!