Ladder logic
Industrial PLC programs in IEC 61131-3 ladder logic. Allen-Bradley tag+address+description labels, Set/Reset coil pairs, parallel branches, seal-in circuits. For manufacturing and automation.
Line 7: invalid element syntax: branch: OTL(SYS_AUTO, "BIT 3.1", name="System Auto Mode")
IEC 61131-3:2013 Ladder Diagram language specification + NEMA ICS 1-2009 North American standard.
Primary References:
- IEC 61131-3:2013 — Programmable controllers, Part 3: Programming languages (Ladder Diagram is one of 5)
- NEMA ICS 1-2009 — General Standards for Industrial Control and Systems (NA standard)
- Allen-Bradley (Rockwell) RSLogix / Studio 5000 ladder conventions
- Siemens TIA Portal ladder conventions
1. Structure & Layout
1.1 Overall Diagram Structure
│ Power Rail L │ │ Power Rail R │
│ │ │ │
│ ├──[element]──[element]──...────────┤ │ ← Rung 0
│ │ │ │
│ ├──[element]──[branch]─────────────┤ │ ← Rung 1
│ │ │ │ │
│ │ [element] │ │
│ │ │ │ │
│ ├───────────────┘─────────────────┤ │ ← Rung 2 (continues)
│ │ │ │1.2 Layout Dimensions
| Parameter | Default | Meaning |
|---|---|---|
power_rail_width | 4px | Power rail stroke width |
rung_height | 70px | Height of each rung |
rung_spacing | 15px | Gap between rungs |
element_width | 32px | Standard element width |
element_height | 24px | Standard element height (contacts/coils) |
rung_wire_y_offset | rung_height / 2 | Rung horizontal wire center |
element_spacing | 12px | Gap between adjacent elements |
left_rail_x | 20px | Left power rail x coordinate |
right_rail_x | canvas_width - 20 | Right power rail x coordinate |
label_offset | 14px | Vertical distance from label to element center |
2. Element Symbols
2.1 Contacts (Inputs — Read conditions)
Contacts are located in the left region of the rung and represent input conditions.
Normally Open Contact (XIC / NO)
- Symbol: Two parallel vertical lines, 8px apart, not connected
- Meaning: When associated bit = 1, the contact closes (conducts)
- SVG:
<!-- Left blade --> <line x1="0" y1="-12" x2="0" y2="12" stroke="#333" stroke-width="2"/> <!-- Right blade --> <line x1="8" y1="-12" x2="8" y2="12" stroke="#333" stroke-width="2"/> <!-- Lead wires (connecting to rung line) --> <line x1="-12" y1="0" x2="0" y2="0" stroke="#333" stroke-width="2"/> <line x1="8" y1="0" x2="20" y2="0" stroke="#333" stroke-width="2"/> <!-- Total width: 32px (−12 to 20) -->
Normally Closed Contact (XIO / NC)
- Symbol: Two parallel vertical lines + diagonal slash through the gap
- SVG: Same as NO + extra diagonal line:
<!-- Diagonal slash across gap --> <line x1="0" y1="6" x2="8" y2="-6" stroke="#333" stroke-width="2"/>
Positive Transition Contact (ONS / Rising Edge)
- Symbol: Two vertical lines + rising-edge arrow (↑)
- SVG: NO +
<polygon points="4,-6 4,0 8,-3"/>inside gap
Negative Transition Contact (ONF / Falling Edge)
- Symbol: Two vertical lines + falling-edge arrow (↓)
- SVG: NO +
<polygon points="4,6 4,0 8,3"/>inside gap
Comparison Contact (EQU, GRT, LES, etc.)
- Symbol: Rectangle box (24×18px) containing comparison operator (
=,>,<,≥,≤,≠)<rect x="0" y="-9" width="24" height="18" fill="white" stroke="#333" stroke-width="1.5"/> <text x="12" y="5" font-size="11" text-anchor="middle">≥</text> <!-- Input/output label references outside the box -->
2.2 Coils (Outputs — Set outputs)
Coils are located on the right side of the rung (adjacent to the right power rail) and represent output drives.
Output Coil (OTE / Standard)
- Symbol: Parentheses
( )— two opposing arcs - SVG:
<!-- Left arc --> <path d="M 0,0 A 8,12 0 0 0 0,0" fill="none"/> <!-- Cleaner: two arcs forming parentheses shape --> <path d="M 8,-12 Q 0,-12 0,0 Q 0,12 8,12" fill="none" stroke="#333" stroke-width="2"/> <path d="M 16,-12 Q 24,-12 24,0 Q 24,12 16,12" fill="none" stroke="#333" stroke-width="2"/> <!-- Lead wires --> <line x1="-12" y1="0" x2="8" y2="0" stroke="#333" stroke-width="2"/> <line x1="16" y1="0" x2="28" y2="0" stroke="#333" stroke-width="2"/> <!-- Total width: 40px -->
Negated Output Coil (OTN)
- Same as OTE, with a
/slash orNOTannotation inside - SVG:
<line x1="8" y1="8" x2="16" y2="-8" stroke="#333" stroke-width="1.5"/>
Set Coil / Latch (OTL)
- Same as OTE, with
Stext inside - SVG:
<text x="12" y="5" font-size="12" font-weight="bold" text-anchor="middle">S</text>
Reset / Unlatch Coil (OTU)
- Same as OTE, with
Rtext inside - SVG: Same as above, text
R
Transition Coils (ONS/ONR)
- Rising-edge/falling-edge triggered, with arrow marker inside
2.3 Function Blocks (Complex functional elements)
Function blocks appear in the middle of a rung; a rectangle box contains input/output pins.
Standard size: 60px wide × 50px tall (minimum), expands based on pin count.
<!-- Function block box -->
<rect x="0" y="0" width="60" height="50"
fill="white" stroke="#333" stroke-width="2"
class="lt-function-block"/>
<!-- Block type label (top center) -->
<text x="30" y="14" font-size="11" font-weight="bold"
text-anchor="middle" class="lt-fb-type">TON</text>
<!-- Divider line under type label -->
<line x1="0" y1="18" x2="60" y2="18" stroke="#333" stroke-width="1"/>
<!-- Input pin labels (left side) -->
<text x="4" y="28" font-size="9">IN</text>
<text x="4" y="40" font-size="9">PT</text>
<!-- Input pin wires (left edge) -->
<line x1="-10" y1="25" x2="0" y2="25" stroke="#333" stroke-width="1.5"/>
<line x1="-10" y1="37" x2="0" y2="37" stroke="#333" stroke-width="1.5"/>
<!-- Output pin labels (right side) -->
<text x="36" y="28" font-size="9">Q</text>
<text x="36" y="40" font-size="9">ET</text>
<!-- Output pin wires (right edge) -->
<line x1="60" y1="25" x2="70" y2="25" stroke="#333" stroke-width="1.5"/>
<line x1="60" y1="37" x2="70" y2="37" stroke="#333" stroke-width="1.5"/>Timer Blocks
| Block | Type | Pins (Input) | Pins (Output) |
|---|---|---|---|
TON | Timer On-Delay | IN, PT (preset time) | Q (done), ET (elapsed) |
TOFF | Timer Off-Delay | IN, PT | Q, ET |
TP | Timer Pulse | IN, PT | Q, ET |
RTO | Retentive Timer On | IN, PT, R (reset) | Q, ET |
Counter Blocks
| Block | Type | Pins (Input) | Pins (Output) |
|---|---|---|---|
CTU | Count Up | CU (count up), R (reset), PV (preset) | Q (done), CV (current) |
CTD | Count Down | CD, LD (load), PV | Q, CV |
CTUD | Up/Down | CU, CD, R, LD, PV | QU, QD, CV |
Math Blocks
Rectangle box, width=60, height=40, labeled: ADD, SUB, MUL, DIV.
Pins: IN1, IN2 (inputs), OUT (output), EN (enable), ENO (enable output).
Comparison Blocks
Labeled: EQU, NEQ, GRT, GEQ, LES, LEQ.
Pins: IN1, IN2, OUT (boolean result).
3. Variable Labels & Addressing
3.1 Label Positioning
Each contact/coil displays the variable address/tag name above or below:
<!-- Tag name above element -->
<text x="x_center" y="y_rung - element_height/2 - 4"
font-size="9" font-family="monospace" text-anchor="middle"
class="lt-tag-label">StartBtn</text>
<!-- Address below element (optional) -->
<text x="x_center" y="y_rung + element_height/2 + 12"
font-size="8" text-anchor="middle" class="lt-address-label">I:0/0</text>3.2 Rung Numbers
Rung numbers are displayed outside the left power rail:
<text x="left_rail_x - 8" y="y_rung" font-size="10"
text-anchor="end" class="lt-rung-number">0001</text>3.3 Rung Comments
Optional comment line above the rung:
<text x="left_rail_x + 10" y="y_rung - rung_height/2 + 8"
font-size="10" font-style="italic" fill="#555"
class="lt-rung-comment">Motor start/stop latch logic</text>4. Parallel Branches
Parallel branches in a rung represent OR logic:
4.1 Branch Start/End Notation
<!-- Branch Start: vertical line down from rung wire -->
<line x1="x_branch_start" y1="y_rung" x2="x_branch_start" y2="y_branch"
stroke="#333" stroke-width="2"/>
<!-- Branch parallel wire -->
<line x1="x_branch_start" y1="y_branch" x2="x_branch_end" y2="y_branch"
stroke="#333" stroke-width="2"/>
<!-- Elements on branch wire (at y_branch level) -->
<!-- Branch End: vertical line back up to rung wire -->
<line x1="x_branch_end" y1="y_branch" x2="x_branch_end" y2="y_rung"
stroke="#333" stroke-width="2"/>4.2 Multiple Parallel Branches
Each additional branch is placed at y_branch + branch_spacing, with default branch_spacing = rung_height + 10.
5. DSL Grammar (Ladder Logic)
document = header statement*
header = "ladder" quoted_string? NEWLINE
statement = comment | variable_decl | rung_def
comment = "#" [^\n]* NEWLINE
variable_decl = "var" id ":" data_type ("=" init_value)? NEWLINE
data_type = "bool" | "int" | "float" | "timer" | "counter" | "string"
init_value = "true" | "false" | INT | FLOAT | quoted_string
rung_def = "rung" INT? rung_comment? ":" NEWLINE INDENT
rung_element+
DEDENT
rung_comment = quoted_string
rung_element = series_element
| parallel_block
series_element = contact | coil | function_block | jump
parallel_block = "parallel:" NEWLINE INDENT
(series_element+)
("branch:" NEWLINE INDENT series_element+ DEDENT)+
DEDENT
contact = contact_type "(" IDENTIFIER ")" NEWLINE
contact_type = "XIC" | "XIO" | "ONS" | "ONF" # NO, NC, rising, falling edge
| "EQU" | "NEQ" | "GRT" | "GEQ" | "LES" | "LEQ" # compare
coil = coil_type "(" IDENTIFIER ")" NEWLINE
coil_type = "OTE" | "OTN" | "OTL" | "OTU" # output, negated, set, reset
function_block = fb_type "(" fb_params ")" NEWLINE
fb_type = "TON" | "TOFF" | "TP" | "RTO"
| "CTU" | "CTD" | "CTUD"
| "ADD" | "SUB" | "MUL" | "DIV" | "MOV"
fb_params = IDENTIFIER ("," IDENTIFIER)*
jump = "JMP" "(" LABEL_ID ")" NEWLINE
| "LBL" "(" LABEL_ID ")" NEWLINE
IDENTIFIER = /[a-zA-Z][a-zA-Z0-9_]*/
LABEL_ID = /[a-zA-Z][a-zA-Z0-9_]*/
INT = /[0-9]+/
FLOAT = /[0-9]+\.[0-9]+/
quoted_string = '"' /[^"]*/ '"'
INDENT = increase in whitespace
DEDENT = decrease in whitespace
NEWLINE = /\n/DSL Example (Motor Start/Stop):
ladder "Motor Control"
var StartBtn: bool
var StopBtn: bool
var EmergencyStop: bool = false
var MotorLatch: bool = false
var MotorRun: bool = false
var RunIndicator: bool = false
rung 0 "Start latch with stop condition":
XIC(StartBtn)
XIO(StopBtn)
XIO(EmergencyStop)
OTL(MotorLatch)
rung 1 "Unlatch on E-stop":
XIC(EmergencyStop)
OTU(MotorLatch)
rung 2 "Motor run output":
XIC(MotorLatch)
OTE(MotorRun)
rung 3 "Run indicator":
XIC(MotorRun)
OTE(RunIndicator)DSL Example (Timer + Counter):
ladder "Timer Counter Example"
var RunSignal: bool
var RunTimer: timer
var CycleCounter: counter
var AlarmOut: bool
rung 0 "TON timer when running":
XIC(RunSignal)
TON(RunTimer, RunSignal, T#5s)
rung 1 "Count on timer done":
XIC(RunTimer.Q)
CTU(CycleCounter, RunTimer.Q, 100)
rung 2 "Alarm when max cycles":
XIC(CycleCounter.Q)
OTE(AlarmOut)DSL Example (Parallel Branch — OR logic):
ladder "Parallel Branch"
rung 0 "Start from push button OR remote":
parallel:
XIC(LocalStart)
branch:
XIC(RemoteStart)
XIC(MotorLatch)
XIO(StopBtn)
OTE(MotorRun)6. SVG Structure
<svg class="lt-ladder" data-diagram-type="ladder">
<defs>
<style>
.lt-power-rail { stroke: #333; stroke-width: 4; fill: none; }
.lt-rung-wire { stroke: #333; stroke-width: 2; fill: none; }
.lt-contact { stroke: #333; stroke-width: 2; fill: none; }
.lt-coil { stroke: #333; stroke-width: 2; fill: none; }
.lt-fb-box { stroke: #333; stroke-width: 2; fill: white; }
.lt-fb-type { font: bold 11px monospace; fill: #333; }
.lt-tag-label { font: 9px monospace; fill: #333; }
.lt-address { font: 8px monospace; fill: #666; }
.lt-rung-number { font: 10px monospace; fill: #999; }
.lt-rung-comment { font: italic 10px sans-serif; fill: #555; }
.lt-branch-wire { stroke: #333; stroke-width: 2; fill: none; }
</style>
</defs>
<title>Ladder Logic Diagram — [name]</title>
<desc>[description]</desc>
<!-- Left power rail -->
<line class="lt-power-rail" x1="20" y1="0" x2="20" y2="{total_h}"/>
<!-- Right power rail -->
<line class="lt-power-rail" x1="{w-20}" y1="0" x2="{w-20}" y2="{total_h}"/>
<!-- Rungs -->
<g id="lt-rungs">
<g id="rung-0" data-rung="0">
<!-- Rung number -->
<text class="lt-rung-number" ...>0000</text>
<!-- Rung comment -->
<text class="lt-rung-comment" ...>Motor start/stop</text>
<!-- Rung horizontal wire -->
<line class="lt-rung-wire" x1="20" y1="y" x2="{w-20}" y2="y"/>
<!-- Elements in rung -->
<g id="rung-0-elem-0" data-type="XIC" data-tag="StartBtn">
<!-- contact SVG -->
<text class="lt-tag-label" ...>StartBtn</text>
</g>
<g id="rung-0-elem-1" data-type="OTL" data-tag="MotorLatch">
<!-- coil SVG -->
<text class="lt-tag-label" ...>MotorLatch</text>
</g>
</g>
</g>
</svg>7. Test Cases
Case 1: Single Contact → Single Coil
ladder
rung 0:
XIC(MotorStart)
OTE(MotorOut)Verify: one rung, NO contact on the left, output coil on the right, both ends connected to power rails.
Case 2: Series Contacts (AND logic)
ladder
rung 0:
XIC(A)
XIC(B)
XIO(C)
OTE(Y)Verify: A/B/C three contacts in series, C is NC (has diagonal slash).
Case 3: Parallel Branch (OR logic)
ladder
rung 0:
parallel:
XIC(Btn1)
branch:
XIC(Btn2)
OTE(Lamp)Verify: Btn1 and Btn2 are on parallel branches that merge before the Lamp coil.
Case 4: TON Timer
ladder
var RunTimer: timer
rung 0:
XIC(StartSignal)
TON(RunTimer, StartSignal, T#10s)
rung 1:
XIC(RunTimer.Q)
OTE(DoneOutput)Verify: TON block shows IN and PT input pins, Q output pin, T#10s parameter annotated.
Case 5: Set/Reset Latch
ladder
rung 0:
XIC(SetBtn)
OTL(MotorLatch)
rung 1:
XIC(ResetBtn)
OTU(MotorLatch)Verify: OTL coil shows S inside, OTU coil shows R inside.
Case 6: Counter
ladder
var PulseCnt: counter
rung 0:
XIC(PulseInput)
CTU(PulseCnt, PulseInput, 50)
rung 1:
XIC(PulseCnt.Q)
OTE(BatchDone)Verify: CTU block has CU/PV inputs, Q output, rung 1 reads the .Q bit.
8. Implementation Priority
| Priority | Feature | Complexity | User Value |
|---|---|---|---|
| P0 (MVP) | XIC, XIO contacts + OTE coil | Low | Core — 90% of rungs use these three |
| P0 | Power rails + rung horizontal wire | Low | Core |
| P0 | Series elements layout | Low | Core |
| P0 | Tag name labels above elements | Low | Core |
| P1 | OTL (Set) / OTU (Reset) coils | Low | High |
| P1 | Parallel branch (OR logic) | Medium | High — required for industrial logic |
| P1 | TON / TOFF timer blocks | Medium | High |
| P1 | CTU counter block | Medium | High |
| P1 | ONS / ONF transition contacts | Low | Medium |
| P2 | Comparison contacts (EQU, GRT, etc.) | Medium | Medium |
| P2 | Math blocks (ADD, SUB, MOV) | Medium | Medium |
| P2 | Rung comments + rung numbers | Low | Medium |
| P2 | CTUD up/down counter | Medium | Low |
| P3 | JMP / LBL jump instructions | Medium | Low |
| P3 | IEC symbol style (vs NEMA) | Medium | Low |
Circuit schematic
Analog and digital circuit schematics from SPICE-style netlist or positional DSL. Auto-routed power/ground rails, orthogonal signal wiring, IEEE standard component symbols.
Single-line diagram (SLD)
Power system one-line diagrams following IEEE 315. Substation and distribution layouts with transformers, breakers, disconnects, buses, and protective relays. For electrical engineering, utilities, and power distribution.