PCB_TCL circuit board description language

* Overview

* Hw_script
* Preload
* Musplay
* Mixer
* Cdman
* Tgt-edif
* fx2_programmer
* Software Radio
* LIGO viewer
* DAQ system

* Status
* CVS
* Download

* About me

x

Overview

There are many packages for creating electronic boards, both open source or not, so why write another ? The main distinction of this package is that it allows to define circuits programmatically in a way convenient enough to create large functional circuits.

There are several benefits to this approach:

  • Loops and conditional expressions allow many tedious tasks to be automated.
  • Separating subcircuits into functions eases modularization of development.
  • One can use source control system to track design changes, as well as standard text tools such as grep and sed.
  • It makes life easy for people who can create code faster than draw lines on the screen. This is especially so when creating boards with lots of digital connections.

The present iteration of the code is a library of Tcl/Tk functions that produce an input file for the popular PCB circuit layout program. The designer is thus free to combine programmatic and free hand approach to creating circuit boards. One can also use autorouter and Gerber export features of the PCB program.

Creating boards in PCB_TCL

To create a board I usually copy the *.tcl scripts that comprise PCB_TCL into a new directory and then create a new board description file that looks like this:


source "library.tcl"

PCB "my_board" 16cm 10cm
DRC space 7mil  width 7mil overlap 5mil silkwidth 8mil
#
# The board uses only 2 layers, however pcb wants two extra for silk and rat lines
#
LAYERS total 4 solder {1} component {2} names {
	1 "solder"
	2 "component"
	}
#STYLE "Signal" thickness 11mil diameter 60mil hole 36mil
STYLE "Signal" thickness 12mil diameter 48mil hole 24mil
STYLE "Power" thickness 1mm diameter 60mil hole 36mil
STYLE "Fat" thickness 25mil diameter 60mil hole 36mil
STYLE "Skinny" thickness 9mil diameter 36mil hole 24mil
STYLE "Signal_via" thickness 12mil diameter 36mil hole 20mil

DEFAULT_LAYER "component"
MAKE_TEXT 105mm 4mm 0 "my_board 0.0.1"

#
# Use ${prefix}_INPUT*2 for input, ${prefix}_OUTPUT*2 for output
#
proc RC_filter { prefix x y net_input net_output {c1 "0.1nF"} {r1 "1k"}} {
PUSH_SETTINGS

MOVE_OFFSET $x $y

DEFAULT_LAYER "solder"

MAKE_RECT {0mm 0mm} {25mm 10mm}

DEFAULT_LAYER "component"
DEFAULT_STYLE "Signal"

PART "${prefix}_INPUT" "Input" "term 2x1" 2mm 5mm 90 [make_test_point_block 2 1]
make_thermal_h "solder" "Power" "${prefix}_INPUT*1"

PART "${prefix}_OUTPUT" "Output" "term 2x1" 23mm 5mm 90 [make_test_point_block 2 1]
make_thermal_h "solder" "Power" "${prefix}_OUTPUT*1"

PART "${prefix}_r1" "r1" "$r1" 9mm 6mm 0 [make_vert_res]

PART "${prefix}_c1" "c1" "$c1" 17mm 4mm 90 [make_small_cap]
make_thermal_h "solder" "Power" "${prefix}_c1*1"

CONNECT "GND" [list "${prefix}_INPUT*1" "${prefix}_OUTPUT*1" "${prefix}_c1*1"]
CONNECT $net_input [list "${prefix}_r1:*:1" "${prefix}_INPUT:*:2"]
CONNECT $net_output [list "${prefix}_r1:*:2" "${prefix}_c1*2" "${prefix}_OUTPUT:*:2"]

MAKE_PATH [LEAD_CENTER "${prefix}_INPUT:*:2"] {} [LEAD_CENTER "${prefix}_r1:*:1"]
MAKE_PATH [LEAD_CENTER "${prefix}_r1:*:2"] {} [LEAD_CENTER "${prefix}_c1*2"]
MAKE_PATH [LEAD_CENTER "${prefix}_c1*2"] {} [LEAD_CENTER "${prefix}_OUTPUT:*:2"]


POP_SETTINGS
}

DEFAULT_STYLE "Signal"
DEFAULT_LAYER "solder"

MAKE_RECT {2mm 2mm} {158mm 98mm}

DEFAULT_LAYER "component"

set i_spacing [list 3.8mm  [expr [dim2mil 16cm]-[dim2mil 3.8mm]]]
set j_spacing [list 3.8mm  [expr [dim2mil 10cm]-[dim2mil 3.8mm]]]

for { set i 0 } { $i < [llength $i_spacing] } { incr i } {
	for {set j 0 } { $j< [llength $j_spacing]} { incr j } {
		PART "H$i$j" "H$i$j" "hole" \
			[lindex $i_spacing $i] [lindex $j_spacing $j]\
			0 [make_hole 3.7mm 2mm 0.9mm]
		}
	}

PART "in_db9" "out_db9" "DB9" 10mm 30mm 90 [make_DB 9]
PART "out_db9" "out_db9" "DB9" 150mm 30mm 90 [make_DB 9]
make_thermal_h "solder" "Power" "in_db9*:1" 1.5mm
make_thermal_h "solder" "Power" "out_db9*:1" 1.5mm

set pin_list {5 9 4 8 3 7 2 6}
set offsets {12 10 2 4 6 8 10 12}

for { set i 0 } { $i < 8 } { incr i } {
	set db9_pin [lindex $pin_list $i]

	RC_filter "filter$db9_pin" 30mm [expr $i*11+5]mm \
		filter_input$db9_pin filter_output$db9_pin
	CONNECT filter_input$db9_pin [list "in_db9:*:$db9_pin"]
	CONNECT filter_output$db9_pin [list "out_db9:*:$db9_pin"]

	MAKE_PATH [LEAD_CENTER "in_db9:*:$db9_pin"] \
		[list E N E[lindex $offsets $i]mm] \
		[LEAD_CENTER "filter${db9_pin}_INPUT:*:2"]

	MAKE_PATH [LEAD_CENTER "filter${db9_pin}_OUTPUT:*:2"] \
		[list E[lindex $offsets $i]mm N E] \
		[LEAD_CENTER "out_db9:*:$db9_pin"]
	}

# Prototyping area

PART "proto1" "proto1" "holes 40x30" 110mm 72mm 0 [make_test_point_block 30 19]

This code produces board shown at the top of the page (click on the image to see larger version).

PCB_TCL in detail

Each board definition starts with the preamble like this:
source "library.tcl"

PCB "my_board" 16cm 10cm
DRC space 7mil  width 7mil overlap 5mil silkwidth 8mil
#
# The board uses only 2 layers, however pcb wants two extra for silk and rat lines
#
LAYERS total 4 solder {1} component {2} names {
	1 "solder"
	2 "component"
	}
#STYLE "Signal" thickness 11mil diameter 60mil hole 36mil
STYLE "Signal" thickness 12mil diameter 48mil hole 24mil
STYLE "Power" thickness 1mm diameter 60mil hole 36mil
STYLE "Fat" thickness 25mil diameter 60mil hole 36mil
STYLE "Skinny" thickness 9mil diameter 36mil hole 24mil
STYLE "Signal_via" thickness 12mil diameter 36mil hole 20mil

First we source library.tcl where I keep convenience functions that define common elements and that is typically shared between projects. Next, we specify board name and dimensions, followed by design requirement check statement that specifies minimal spacing between traces, minimum trace width, minimal overlap to consider two traces joined and width of lines on the silk screen. Adjust these to match capabilities of your favourite PCB factory (I have successfully used CustomPCB and Futurlec in the past).

Then we define layers comprising our board. This example uses just two which makes for an inexpensive design that is easy to retrofit, but up to 8 copper layers can be used. A quirk that I have not gotten around to fixing yet is that the number of layers one specifies should be 2 more, as PCB views silkscreen and rat lines as additional layers.

Lastly we define styles of PCB traces that would be used. In this example we have a very thick Power style, a tiny Skinny style and several intermediate variants - I usually copy'n'paste this between designs.

The preamble is followed by statements that actually defined the board. Each statement is just a Tcl command so they can be freely mixed with Tcl language constructs such as loops or arithmetic:

set pin_list {5 9 4 8 3 7 2 6 1}
set offsets {12 10 2 4 6 8 10 12 14}

for { set i 0 } { $i < 9 } { incr i } {
	set db9_pin [lindex $pin_list $i]

	RC_filter "filter$db9_pin" 30mm [expr $i*10+5]mm \
		filter_input$db9_pin filter_output$db9_pin
	CONNECT filter_input$db9_pin [list "in_db9:*:$db9_pin"]
	CONNECT filter_output$db9_pin [list "out_db9:*:$db9_pin"]

	MAKE_PATH [LEAD_CENTER "in_db9:*:$db9_pin"] \
		[list E N E[lindex $offsets $i]mm] \
		[LEAD_CENTER "filter${db9_pin}_INPUT:*:2"]

	MAKE_PATH [LEAD_CENTER "filter${db9_pin}_OUTPUT:*:2"] \
		[list E[lindex $offsets $i]mm N E] \
		[LEAD_CENTER "out_db9:*:$db9_pin"]
	}

As usual in programming one can create functions that perform various tasks - from making an array of holes to complete functional blocks. This example has an RC filter function.

PCB_TCL commands

PART "Name" "Description" "Value" x y angle ELEMENT

The PART command places a new electronic part on the board. Most arguments are self-explanatory. The position can be specified using either mils (1/1000 of an inch) as raw number, or suffixed by "mm" or "in" to use millimeters or inches. The angle is specified in degrees and has to be a multiple of 90.

The element construct is produced by built-in make_element function, see example usage in library.tcl

An earlier version of this directive omitted "Value" field, which is useful for producing parts list.

MAKE_PATH [LEAD_CENTER "pin_spec1"] {hints} [LEAD_CENTER "pcb_spec2"]

The MAKE_PATH command makes traces on the pcb. It takes three arguments: start, hints and destination. The start and destination are usually specified using LEAD_CENTER or VIA_CENTER functions that find the center of a pin or a pad of integrated circuit or a via. However, they can simply be a list of two numbers (possibly with mm or in qualifiers).

The pin specification has three parts separated by colons: part name, symbolic pin name and numeric pin name. Wildcard "*" can be placed anywhere in specification. An error is issued if more then one pin was matched.

The hints argument describes how the path should be made. It is a list which can contain zero or more of directions N, S, E, W, NE, NW, SE, SW followed by optional length specifier. For example, it could look like this {N NW1mm W} - go north (up), go north-west (up and left) by 1 millimeter then go west (left). The unspecified lengths are automatically adjusted to connect the source and destination points.

One can also specify hints as proportions. For example, the specifier {2N NW10mm 1N} creates a north trace which goes north two thirds of the way, then diagonally for 10mm, and then continues north for one third of the length minus 10mm*sqrt(2).

CONNECT "Net name" [list "pin_spec1" "pin_spec2" "pin_spec3"]

The CONNECT directive is used the specify the netlist - which pins are connected. This allows PCB to check mistakes in wiring, as well as engage autorouter for any connections not made explicitly. I typically use auto-router for "do-not-care" connections, such as slow digital control pins, I2C, etc. Sometimes PCB autorouter needs help by partitioning the board into easy to do pieces.

I typically use "GND" for ground. The pin specifications can match more than one pin, for example "*:GND:*" will match pins and pads on all integrated circuits that have symbolic name "GND" - a great help when wiring digital circuits.

POP_SETTINGS
...
PUSH_SETTINGS

This pair saves and restores the drawing context on the stack. Convenient to use in functions and anyplace you need to make a bunch of small tweaks to usual drawing mode, such as changing signal style, board side where parts are placed, etc.

MOVE_OFFSET $x $y

Offset future drawing operations for this amount.

MAKE_RECT {0mm 0mm} {25mm 10mm}

Draw solid rectangle on the currently active layer. Useful for creating ground and power planes.

MAKE_TEXT x y angle "text"

Draw text at location (x,y). Angle must be a multiple of 90 degrees.

DEFAULT_LAYER "component"
DEFAULT_STYLE "Signal"

Switch default layer and style to specified values. They should match names declared in the preamble. Case sensitive.

make_thermal "solder" "Power" "pin_spec" [size]
make_thermal_h "solder" "Power" "pin_spec" [size]
make_thermal_v "solder" "Power" "pin_spec" [size]

Connect pin given by pin_spec to ground or power plane at the specified layer using specified drawing style. Draw either a cross or a vertical or horizontal line across the pin. The optional argument size specifies the length of the line - useful when pins are tightly spaced.

PLACE_VIA_NEAR_LEADS [list pin_spec1 pin_spec2 ... ] {hints}

Make a path from pins given by pin_spec1, pin_spec2, ... given by hints (same as in MAKE_PATH command) and put a via at the end of each path. Useful for modern surface mount chips. The vias created would be named "VIA_NEAR_$pin_spec1" and so on, but with wildcards expanded.

SourceForge Logo http://volodya-project.sourceforge.net/pcb_tcl.php was last modified 1:30pm Tuesday, July 08th 2014