IEC 61131-3: The Complete Guide to PLC Programming Languages

By | July 20, 2026

Every PLC vendor has its own toolchain, but they largely follow the same programming model. That’s because of IEC 61131-3. It defines the programming languages, the data types, and the software architecture that PLCs and their programming tools are built around. The goal is portability — write logic against the standard, and it should carry across platforms with far less rework than a proprietary approach.

Before anything else, get the edition right, because most material online is out of date. The current version is the fourth edition, published in May 2025, which cancelled and replaced the 2013 third edition. Between those two revisions the standard changed a lot: the third edition added object-oriented programming and namespaces, and the fourth edition added support for UTF-8 encoded strings and related functions, and removed one of the original languages outright. If your reference still describes “five equal languages,” it’s two editions behind.

This guide covers the whole standard as it stands now — the software model, the building blocks, data types, the standard function block library, the languages, object orientation, and what each recent edition changed.

The software model

Start with the architecture, because the languages sit inside it.

The standard describes a layered software model. At the top is the configuration — the whole software setup for one PLC system. Inside a configuration are one or more resources, each roughly a processor that can run code. A resource runs programs, and programs are scheduled by tasks, which control how often and under what conditions code executes — cyclic, on an interval, or triggered by an event.

Programs are built from smaller reusable units, and those units are where the real work happens.

The building blocks: Program Organization Units

The standard calls them Program Organization Units, or POUs. There are three, and the difference between them is the single most useful thing to understand about 61131-3.

A function takes inputs and returns one result, with no memory between calls. Same inputs, same output, every time. Think ADD, SQRT, a type conversion. It has no internal state to carry forward.

A function block has memory. Each instance keeps its internal variables between executions, so its output depends on both the current inputs and what happened before. A timer is the classic example — it has to remember elapsed time. Because instances hold state, you declare a function block once and instantiate it as many times as you need, each copy tracking its own values.

A program is the top-level POU. It ties function and function block instances together into the logic a task actually runs.

Get this distinction and most of the standard falls into place: functions for stateless math and conversion, function blocks for anything that has to remember, programs to assemble them.

Data types

61131-3 is strongly typed. Every variable has a declared type, and the standard fixes a common set so a BOOL or a DINT behaves the same on any compliant system. The types come in three tiers.

Elementary data types

These are the built-in primitives. Group them by what they hold.

Booleans and bit strings. BOOL holds a single bit — TRUE or FALSE. The bit strings hold fixed-width groups of bits you can address individually: BYTE (8), WORD (16), DWORD (32), and LWORD (64). Use bit strings when you’re masking, packing flags, or working at the register level rather than treating the value as a number.

Integers. The signed family runs SINT (8-bit), INT (16-bit), DINT (32-bit), and LINT (64-bit). Each has an unsigned counterpart — USINT, UINT, UDINT, ULINT — that trades the negative range for double the positive range.

Real numbers. REAL is 32-bit single-precision floating point, LREAL is 64-bit double-precision. Although both follow IEC 60559 (IEEE 754), vendors may differ in how they handle exceptional values such as NaN or infinity.

Time and date. TIME is a duration. DATE, TIME_OF_DAY (TOD), and DATE_AND_TIME (DT) cover calendar dates and clock times. The third edition added 64-bit versions with nanosecond resolution — LTIME, LDATE, LDT, and LTOD — for finer timestamps.

Characters and strings. STRING stores sequences of single-byte characters, while WSTRING stores wide characters encoded according to ISO/IEC 10646. The third edition added single-character types CHAR and WCHAR, and the 2025 fourth edition added support for UTF-8 encoded strings and related string functions. You index into a string with square brackets starting at position 1, so MyString[2] is the second character. Maximum length is implementer-specific and set in the declaration.

Generic data types

Generic types are categories, not types you declare variables with. The standard organizes elementary types into a hierarchy under ANY, and uses that hierarchy to define overloaded standard functions — an ADD that accepts any numeric type, for example.

The tree runs from ANY down through ANY_ELEMENTARY, then splits: ANY_NUM covers the numbers and divides into ANY_INT and ANY_REAL; ANY_BIT covers BOOL and the bit strings; and there are branches for ANY_STRING, ANY_CHAR, ANY_DATE, and ANY_DURATION. You won’t write ANY_NUM in your own declarations — it explains why one standard function can accept many types.

User-defined data types

This is where you extend the type system, using the TYPE ... END_TYPE construct. A user-defined type can go anywhere a base type can.

Enumerations define a named set of allowed values — a signal range, a machine mode, a state — so you stop tracking magic numbers. Enumerations with explicit values, a third-edition addition, let you back each name with a specific typed value. Subrange types constrain an integer to a range, like INT(-4095..4095). Arrays hold indexed collections of one type, with variable-length arrays added in the third edition. Structures group related fields of different types into one named type. Aliases give an existing type a new name for readability. Any user-defined type can carry an initial value, assigned with :=.

Variables and scope

Every variable is declared inside a POU, in a typed declaration block. The block you put it in sets its scope and its role, so the keyword matters as much as the type.

Internal working variables go in VAR. The interface uses VAR_INPUT for values passed in and read-only inside, VAR_OUTPUT for values the POU produces and the caller can read, and VAR_IN_OUT for values passed by reference and modified in place. VAR_GLOBAL declares data shared across the configuration, and VAR_EXTERNAL is how a POU reaches a global declared elsewhere. VAR_TEMP holds temporary values with no retention between calls. One further class, VAR_CONFIG, handles configuration-specific instance initialization — but it’s a specialized construct tied to configuration declarations, not an everyday variable class, and it isn’t supported in every environment.

Three qualifiers change how a variable behaves. CONSTANT makes it read-only. RETAIN keeps its value through a power cycle or warm restart; NON_RETAIN explicitly does not. That distinction matters for anything that has to survive a restart, like a running total or a stored setpoint.

Initialization uses := in the declaration — Level : REAL := 0.0;. Without it, a variable takes the default for its type, and a reference defaults to NULL. Instance-specific values set through VAR_CONFIG override type-level initial values.

You can also tie a variable straight to a physical or memory location with a directly represented address. The prefix gives the area — %I for inputs, %Q for outputs, %M for internal memory — and a size letter follows: X for a bit, B byte, W word, D double word, L long. So %IX1.0 is an input bit and %QW28 an output word. The AT keyword maps a named variable to an address, as in Valve_Pos AT %QW28 : INT;, which keeps the code readable while pinning it to real I/O.

Standard functions

Separate from the function block library, the standard defines a set of functions — stateless operations that return the same result for the same inputs, with no memory. Most are overloaded on the generic type hierarchy, so one name works across many types, and most can be typed explicitly when you want a fixed one. The library breaks into a few families.

Type conversion*_TO_** functions like INT_TO_REAL, plus TRUNC and the named conversions. The third edition tightened which conversions happen implicitly and which you have to write out.

Numerical — single-input math: ABS, SQRT, LN, LOG, EXP, and the trig set SIN, COS, TAN, ASIN, ACOS, ATAN.

ArithmeticADD, SUB, MUL, DIV, MOD, and EXPT. Several double as the operators +, -, *, / in textual code.

Bit and Boolean — shifts SHL, SHR and rotations ROL, ROR, plus the bitwise AND, OR, XOR, NOT.

Selection and comparisonSEL (binary select), MUX (multiplexer), MAX, MIN, LIMIT, and MOVE; and the comparisons GT, GE, EQ, LE, LT, NE. All comparisons except NE are extensible to more than two operands.

Character stringLEN, LEFT, RIGHT, MID, CONCAT, INSERT, DELETE, REPLACE, and FIND.

Time and date — arithmetic on TIME and the date types, plus concatenate and split functions added in the third edition.

The overloading comes straight from the generic ANY hierarchy: a function defined against ANY_NUM accepts any numeric type under it, which is why the same ADD handles INT and REAL alike.

Standard function blocks

You don’t build the common logic from scratch. The standard defines a library of function blocks with fixed behavior, provided by every compliant system, so logic that leans on them ports cleanly. They’re function blocks because they hold state. Four groups cover most day-to-day work.

Bistables (latches). SR is the set-dominant latch — if both inputs are active, set wins and the output stays on. RS is the reset-dominant latch — reset wins. Pick based on which condition has to take priority; reset-dominant is common where “off” is the safe state.

Edge detection. R_TRIG fires on a rising edge — its output goes true for one scan when the input goes 0 to 1, then returns to 0 even if the input stays high. F_TRIG fires on a falling edge. That one-scan pulse lets you trigger an action once per event instead of every scan the signal is held.

Counters. CTU counts up — each pulse on CU increments the current value CV, and output Q turns on when CV reaches the preset PV; input R resets. CTD counts down from a loaded preset, with Q on at zero. CTUD counts both ways with separate inputs and two outputs. Typed variants exist for the count width you need.

Timers. TP is a pulse timer — the output goes true for exactly the preset PT on a trigger, regardless of what the input does after. TON is an on-delay — the output turns on only after the input holds true for PT. TOF is an off-delay — the output stays on for PT after the input drops. Input IN controls the timer, PT sets the duration, Q is the output, and ET reports elapsed time.

The programming languages

Here’s where the “five languages” idea needs correcting for the current edition. The standard now defines three living languages — one textual and two graphical — plus a structuring element that organizes them. The choice between them is mostly about the problem: math and sequencing suit text, discrete logic suits ladder, signal flow suits blocks.

Structured Text (ST)

ST is the high-level textual language, close in feel to Pascal, and now the only standardized textual language. It’s built from expressions and statements.

Expressions follow a fixed operator precedence — from parentheses at the top, down through function and method calls, negation and NOT, the exponent **, then * / MOD, then + -, the comparisons < > <= >=, equality = and <>, and finally &/AND, XOR, and OR at the bottom. Operators of equal precedence evaluate left to right.

The statements cover assignment with :=, calls to functions, function blocks, and methods, and RETURN. Selection uses IF ... ELSIF ... ELSE and CASE. Iteration uses FOR, WHILE, and REPEAT, with EXIT to break a loop and CONTINUE — added in the third edition — to skip to the next pass.

ST is the right tool for arithmetic, string handling, loops, and state machines — anything that would sprawl into a tangle of contacts or blocks.

IF Level > Setpoint THEN
    Pump  := FALSE;
    Alarm := TRUE;
ELSE
    Pump  := TRUE;
END_IF;

Ladder Diagram (LD)

LD is the graphical language modeled on relay ladder logic. Each network is bounded by a left and right power rail, and logic reads as power flowing from the left rail — always considered ON — through elements to the right. A horizontal link passes a state along; a vertical link acts as an OR of the links feeding it.

You read Boolean state with contacts. A normally open contact --| |-- passes power when its variable is true, a normally closed contact --|/|-- passes power when its variable is false, and transition-sensing contacts --|P|-- and --|N|-- pass power for one evaluation on a rising or falling edge. The third edition added compare contacts, typed and overloaded, that pass power when a comparison is true.

You write Boolean state with coils. A normal coil --( )-- sets its variable to the incoming power state, a negated coil --(/)-- inverts it, and set and reset coils --(S)-- and --(R)-- latch and unlatch. Functions and function blocks drop into networks too. LD is strongest for discrete control and interlocks, where the relay metaphor makes intent obvious.

Function Block Diagram (FBD)

FBD is the other graphical language, built around signal flow instead of power flow. Functions and function block instances are drawn as boxes, and you wire outputs to inputs with connection lines that carry signals left to right. Networks evaluate in a defined order, and feedback paths are allowed, so you can build closed loops.

Both Boolean and analog signals travel the same way, which makes FBD a natural fit for continuous processes, closed-loop control, and any logic you’d sketch as a chain of blocks. It shares graphical common elements with LD, including EN/ENO for conditional execution of a block.

Sequential Function Chart (SFC)

SFC is not a standalone language. It’s a structuring element that organizes the other languages into a sequence, and it’s the right way to describe a process that moves through stages — a batch, a startup, a shutdown.

An SFC is built from steps and transitions. A step is one stage of the sequence, drawn as a block or written as STEP ... END_STEP. Each step exposes a flag, Step.X, a Boolean that is true while the step is active, and an elapsed time, Step.T, of type TIME. Every SFC has exactly one initial step, drawn with a double border. A transition sits between steps with a condition; when an active step’s outgoing transition becomes true, the sequence advances.

What a step does is defined by actions, tied to the step through an action block with a qualifier that sets the timing. N runs the action while the step is active, S stores (latches) it, and R resets it. L runs it for a limited time, D runs it after a delay, and P fires it as a pulse, with SD, DS, and SL combining stored behavior with timing. The qualifiers that involve time — L, D, SD, DS, SL — carry a TIME duration. The action bodies themselves are written in any of the other languages. Vendor support for all action qualifiers varies.

Instruction List (IL)

IL was a low-level, assembly-like textual language that used to round out the set. It was deprecated in the 2013 third edition and removed entirely in the 2025 fourth edition. It’s no longer part of the standard, which leaves ST as the only standardized textual language. In practice, most existing PLC engineering tools still support IL for backward compatibility, so you’ll keep encountering it in legacy projects — but that support now sits outside 61131-3 rather than being required by it.

Object-oriented programming

The 2013 third edition brought the biggest conceptual change in the standard’s history — object orientation — and it carries into the current edition.

It’s worth clearing up the terminology first. What IT languages like C++, Java, or C# call a class corresponds to a type here, and what they call an object corresponds to an instance — the same type-and-instance model already used for function blocks. So OOP in 61131-3 extends a model you already know rather than replacing it.

A class is an object-oriented software type that encapsulates variables and methods. Instances of the class provide access to its variables and methods. Vendors surface classes differently across their tools. Access to members is controlled with PUBLIC, PRIVATE, INTERNAL, and PROTECTED, with PROTECTED as the default. A class can inherit from a base class to extend it, and can implement one or more interfaces — an interface being a set of method prototypes a class agrees to provide.

Function blocks gained the same features. An object-oriented function block can carry methods, extend a base block, and override behavior. That gives you inheritance and polymorphism inside the familiar function block model, which is why the OOP additions landed without breaking older code.

A method is a named operation that belongs to a class or function block, with its own inputs, outputs, and local variables, and its own access level. Methods are how you act on an instance’s internal data in a controlled way, rather than exposing the variables directly. Polymorphism follows from inheritance and interfaces: a reference to a base type or an interface can point to any instance that derives from it, and a call through that reference runs the derived version. That lets you write logic against a common type and swap implementations underneath — a set of pump blocks sharing one interface, called the same way regardless of which pump is wired in.

How the standard has changed

Two recent editions reshaped 61131-3, and knowing which did what tells you whether a given reference is still trustworthy.

The 2013 third edition added object-oriented programming — classes, methods, interfaces, and inheritance — plus namespaces for organizing large projects, references (declared with REF_TO and checked against NULL), enumerations with named values, variable-length arrays, and cleaner type conversion rules. It stayed compatible with the earlier edition, so existing code kept working.

The 2025 fourth edition cancelled and replaced the third as a technical revision. Its two headline changes are support for UTF-8 encoded strings and related string functions, and the complete removal of Instruction List, which had been deprecated in 2013. That removal is the change most worth remembering, because it’s the one that dates older summaries: the standard now defines one textual language, not two.

The practical takeaway for anyone specifying or writing against 61131-3 today: treat it as an object-capable, namespaced language family built on ST, LD, and FBD — not the flat five-language picture from a decade ago, and not the IL-inclusive one either.

IEC 61131-3 languages at a glance

LanguageTypeStatusBest for
Structured Text (ST)Textual, high-levelCurrentMath, loops, complex logic
Ladder Diagram (LD)GraphicalCurrentDiscrete and relay-style control
Function Block Diagram (FBD)GraphicalCurrentContinuous processes, signal flow
Sequential Function Chart (SFC)Structuring elementCurrentOrganizing sequential steps
Instruction List (IL)Textual, low-levelRemoved (2025 edition)Legacy code only — no longer standardized

Elementary data types at a glance

CategoryTypesNotes
BooleanBOOLSingle bit, TRUE/FALSE
Bit stringsBYTE, WORD, DWORD, LWORD8, 16, 32, 64 bits
Signed integerSINT, INT, DINT, LINT8, 16, 32, 64 bits
Unsigned integerUSINT, UINT, UDINT, ULINT8, 16, 32, 64 bits
RealREAL, LREAL32 / 64-bit floating point (IEC 60559)
Time and dateTIME, DATE, TOD, DTDuration, date, clock time
Long time and dateLTIME, LDATE, LDT, LTOD64-bit, nanosecond resolution
CharacterCHAR, WCHARSingle character; CHAR single-byte, WCHAR wide (ISO/IEC 10646)
StringSTRING, WSTRINGVendor-defined encoding; UTF-8 support added in the 2025 edition

FAQ

Is IEC 61131-3 the same as IEC 61131?

No. IEC 61131 is a multi-part standard covering programmable controllers as a whole — hardware, communications, and more. Part 3 is the one that defines the programming languages and software model. When people say “61131” about programming, they almost always mean 61131-3.

Which edition is current?

The fourth edition, IEC 61131-3:2025, published in May 2025. It replaced the 2013 third edition. Its main changes are support for UTF-8 encoded strings and related string functions, and the removal of Instruction List.

How many programming languages does IEC 61131-3 define?

Historically five, but the current edition treats that differently. ST, LD, and FBD are the active languages, and SFC is a structuring element used with them. IL was removed in the 2025 edition, leaving ST as the only standardized textual language.

What’s the difference between a function and a function block?

A function has no memory — same inputs always give the same result. A function block keeps internal state between executions, which is why timers, counters, and anything stateful are function blocks. Function blocks are instantiated; each instance tracks its own values.

Does IEC 61131-3 support object-oriented programming?

Yes, since the 2013 third edition. It added classes, methods, interfaces, and inheritance, and extended function blocks with the same features. Earlier editions did not have this, which is why older references don’t mention it.

Is Instruction List still usable?

It was removed from the standard in the 2025 fourth edition, after being deprecated in 2013. Most existing engineering tools still support it for backward compatibility, so existing IL code will run and you’ll still see it in legacy projects — but it’s no longer standardized, and new projects should use ST.

Is IEC 61131-3 vendor-specific?

No — that’s the point of it. It’s a vendor-independent standard aimed at portability. Each vendor’s implementation has quirks and extensions, so code isn’t always perfectly portable, but the shared foundation is far closer than proprietary systems.

Author: Zakaria El Intissar

I've spent 13 years in power system automation, electrical protection, and SCADA communication, as an automation and industrial computing engineer. ScadaProtocols.com is where I turn what I've learned on site into plain guides and working tools — so other engineers can decode, analyze, and troubleshoot industrial communication protocols without the guesswork.