IEC 61131-3: Comparison of Edition 3 and Edition 4

Edition 4 of IEC 61131-3 has been available online since May 2025. In the following post, I will briefly explain the most important differences between Edition 3 and Edition 4.

I was unable to test the new features of Edition 4 as I did not have a development environment that supported the new functions of Edition 4 at the time of the comparison. I can therefore only offer a general overview of the differences. I used IEC 61131-3 (Edition 3.0) 2013-02 and IEC 61131-3 (Edition 4.0) 2025-05 for the comparison. However, if you want or need to familiarise yourself more intensively with IEC 61131-3, there is no getting around the standard.

The first impression

When comparing the tables of contents, it is noticeable that in Edition 3 the appendix begins on page 221, while in Edition 4 the appendix begins on page 241.

A new addition is the 6-page chapter 6.9 Synchronisation of concurrent execution. The corresponding sub-chapters reveal that this is about mutexes and semaphores.

Chapter 7.2 Instruction List (IL) is no longer included in Edition 4. This means that IL is no longer part of IEC 61131-3, but this does not necessarily mean that IL is no longer offered by manufacturers. Each manufacturer can continue to decide for themselves whether they want to offer IL in their development environment or not.

Octal literals

In addition to the integer literals (e.g. -43), the floating point literals (e.g. -43.8), the binary literals (e.g. 2#1101_0110) and the hexadecimal literals (e.g. 16#E26B), there are also octal literals (e.g. 8#267) in Edition 3. The octal literals are no longer supported by Edition 4. In Edition 3, the octal literals were already labelled as deprecated.

USTRING and UCHAR data types

With the two new data types USTRING and UCHAR, character strings are now also supported in which each character is encoded according to UTF-8.

While a WSTRING / WCHAR is encoded as UTF-16 and always requires 2 bytes per character, UTF-8 encodes each character with a sequence of 1 to 4 bytes.

UTF-8, or Unicode Transformation Format – 8 bit, is backwards compatible with 7-bit ASCII, as the first 128 characters in UTF-8 and ASCII are identical in the English character set. This means that every valid ASCII character string is also a valid UTF-8 character string, so that most programmes written for ASCII can be easily adapted to UTF-8.

UTF-8 is particularly widespread in Internet applications. Both the Internet Mail Consortium (IMC) and the Word Wide Web Consortium (W3C) recommend the use of UTF-8 as a standardised encoding.

USTRING and UCHAR literals are specified as follows:

x : USTRING := USTRING#'ABC';
x : USTRING := U#'ABC';
y : UCHAR := UCHAR#'A';
y : UCHAR := U#'A';

LEN_MAX and LEN_CODE_UNIT

The character string functions are described in chapter 6.6.2.5.12. The functions LEN_MAX and LEN_CODE_UNIT are new additions.

LEN_MAX gibt die maximale Länge des Strings zurück:

VAR
   x   : STRING[100] := 'Hello';
   y   : WSTRING[100] := WSTRING#'Hello';
   z   : USTRING[100] := USTRING#'Hello';
END_VAR
a := LEN_MAX(x);   // a = 100
b := LEN_MAX(y);   // b = 100
c := LEN_MAX(z);   // c = 100

LEN_CODE_UNIT returns the number of code units occupied by the string. For the data types STRING and WSTRING, LEN_CODE_UNIT returns the same value as for LEN. With USTRING, however, the value of LEN_CODE_UNIT can be greater than that of LEN.

a := LEN_CODE_UNIT(x);   // a = 5
b := LEN_CODE_UNIT(y);   // b = 5
c := LEN_CODE_UNIT(z);   // c = 5

As the strings in this example only contain ASCII characters, LEN_CODE_UNIT returns the same value for all three data types.

Specification of characters by character code

In a string or character literal, any character can be specified by the character code. This is specified in hexadecimal notation after a $ in curly brackets:

VAR
   x   : STRING := '${9}'; // Control character for tabulator
   y   : WSTRING := WSTRING#'${2211}';  // Character Σ
   z   : USTRING := USTRING#'${1F579}'; // Emoji ‚Joystick’
END_VAR

The previous support of character codes with fixed lengths (e.g. ‘$0A’ or “$00C4”) is still supported.

Note: For the representation of the character code, I would have liked the usual coding for the respective number system to be used, i.e. 16#1F579 instead of just 1F579. Even if it is usual to specify character codes in hexadecimal, it is easier to recognise in which number system the character code was specified.

Unfortunately, the Char Map under Windows only allows the entry of character codes with a maximum of 4 digits. The function Insert -> Symbol in Word is much more helpful in finding the right character code:

The string and character literals are described in chapter 6.3.3 Character string literals.

Data type conversion

Figure 11 in chapter 6.6.1.6 Data type conversion provides an overview of the implicit and explicit data type conversions. Compared to Edition 3, there are some adjustments and extensions here.

Edition 4 defines some explicit data type conversions that are not included in Edition 3:

  • LWORD_TO_BOOL, DWORD_TO_BOOL, WORD_TO_BOOL and BYTE_TO_BOOL
  • UINT_TO_WCHAR and USINT_TO_CHAR
  • WSTRING_TO_WCHAR
  • WCHAR_TO_UINT

Some explicit data type conversions have been changed to implicit data type conversions:

  • STRING_TO_WSTRING
  • CHAR_TO_WCHAR

Some implicit and explicit data type conversions have been added for the USTRING and UCHAR data types:

  • UDINT_TO_UCHAR and DWORD_TO_UCHAR
  • USTRING_TO_WSTRING, USTRING_TO_STRING and USTRING_TO_UCHAR
  • UCHAR_TO_UDINT, UCHAR_TO_LWORD, UCHAR_TO_DWORD, UCHAR_TO_USTRING, UCHAR_TO_WCHAR and UCHAR_TO_CHAR
  • WSTRING_TO_USTRING and STRING_TO_USTRING
  • CHAR_TO_UCHAR

Data type conversion between strings and ARRAY OF BYTE

The functions for data type conversion between strings and ARRAY OF BYTE are new additions. These are described in chapter 6.6.2.5.8 Data type conversion between string types and array of bytes:

  • STRING_TO_ARRAY_OF_BYTE, WSTRING_TO_ARRAY_OF_BYTE and USTRING_TO_ARRAY_OF_BYTE
  • ARRAY_OF_BYTE_TO_STRING, ARRAY_OF_BYTE_TO_WSTRING and ARRAY_OF_BYTE_TO_USTRING

TRUNC was removed

The TRUNC function is defined as a typed and overloaded function. In the typed notation, the source data type and the target data type are specified in the function name, e.g. LREAL_TRUNC_DINT(x), while in the overloaded notation, only the target data type is specified, e.g. TRUNC_DINT(x). The source data type is determined from the transferred parameter.

Edition 3 still had the notation without specifying a data type definiert, i.e. TRUNC(x). Here the source data type always had to be ANY_REAL and the target data type always ANY_INT. This notation was already labelled as deprecated in the edition 3 and has now been removed from edition 4.

Properties

Edition 4 (finally) defines properties. Properties are part of a CLASS, FUNCTION_BLOCK or INTERFACE. Setter and getter methods are declared so that the value of a property can be set or read. The declaration is similar to that of a method. However, the keywords PROPERTY_GET and PROPERTY_SET are used instead of the METHOD keyword.

PROPERTY_GET PUBLIC nFoo : INT
   ;
END_PROPERTY
PROPERTY_SET PUBLIC nFoo : INT
   ;
END_PROPERTY

If setter and getter methods are defined, the data type must be the same for both methods.

The same access specifiers can be used for properties as for methods, i.e. INTERNAL, PRIVATE, PROTECTED and PUBLIC. The keywords ABSTRACT and FINAL are also possible. If no access specifier is specified, the property is PROTECTED.

If a property is only to be readable, the setter method can be omitted or set to PRIVATE.

A property can be used like an instance variable:

VAR
   fbFoo    : FB_Foo;
   x        : INT := 10;
END_VAR
fbFoo.nFoo := x;   // Write to the property
x := fbFoo.nFoo;   // Read from the property

Program code is stored in the setter and getter methods to manage the value of the property internally.

FUNCTION_BLOCK FB_Foo
   VAR PRIVATE
      _nFoo   : INT; // Backing variable
   END_VAR
   PROPERTY_GET PUBLIC nFoo : INT
      nFoo := _nFoo;
   END_PROPERTY
   PROPERTY_SET PUBLIC nFoo : INT
      _nFoo := nFoo;
   END_PROPERTY
END_FUNCTION_BLOCK

The properties are described in chapter 6.6.5.11 Properties.

Assertions

In chapter 6.6.2.5.17 Assertions, Edition 4 defines the ASSERT function, which has the input parameter IN of type BOOL and no return value. ASSERT is used to check the validity of expressions or variables during development.

If IN is TRUE when called, the function has no effect. However, if IN is FALSE, ASSERT should ‘inform’ the developer of this. Each manufacturer (of the development environment) can individually define what the message looks like.

Once development is complete, calling ASSERT no longer has any effect, regardless of the value of the input parameter IN. How this is communicated to the development environment can be determined by the manufacturer.

Synchronization of concurrent execution

Chapter 6.9 Synchronization of concurrent execution has been added in Edition 4 and defines objects and functions to synchronize program code that has been split into several tasks. Synchronization is required, for example, if two parallel programs access a common variable, whereby one program also changes this variable.

Mutexes and Semaphores are defined in Edition 4 to make it easier to synchronize programs running in parallel.

I will only briefly introduce the functions that define Edition 4. A detailed description of these concepts would be too extensive at this point, but could be the subject of a separate post.

Mutex

Mutexes are usually used to model critical sections. A mutex can be locked and unlocked.

Both an object-oriented approach and an approach based on functions are defined. The object-oriented approach is based on the MUTEX function block. This has the following methods:

  • UNLOCK
  • LOCK
  • TRYLOCK

MUTEX also has the properties LOCK_COUNT and OWNER. OWNER is used to store a value that identifies the task.

In the approach based on functions, the corresponding functions are available:

  • MUTEX_UNLOCK
  • MUTEX_LOCK
  • MUTEX_TRYLOCK

Semaphore

Here too, both an object-oriented approach and an approach based on functions are defined. The SEMA function block was defined for the object-oriented approach. This has the methods:

  • RELEASE
  • ACQUIRE
  • TRY_ACQUIRE

The following functions have been defined accordingly:

  • SEMA_RELEASE
  • SEMA_ACQUIRE
  • SEMA_TRY_ACQUIRE

Support of BCD numbers discontinued

In future, BCD numbers will no longer be supported. In Edition 4, the following functions are marked as deprecated:

  • IS_VALID_BCD
  • BCD_TO_out and in_BCD_TO_out
  • TO_BCD_out and in_TO_BCD_out

Conclusion

My personal highlight is the support of mutexes and semaphores. In times when CPUs have more and more cores, appropriate tools must also be provided to distribute a program across several tasks.

I would describe the remaining changes as general care and maintenance. It’s good that things are also removed from the standard if they are no longer relevant. This includes, for example, the removal of IL as and the omission of octal literals.

Some points have also been defined in more detail, for example the question of whether ARRAY[1..1] and ARRAY[1..0] are permitted and how they behave (chapter 6.4.4.5.1).

Otherwise, I don’t see the differences as extensive as between Edition 2 (published in 2003) and Edition 3 (published in 2013).

However, I also miss some points. These include the implicit enumeration, optional parameters for methods and functions, as well as the option to overload methods.

Unknown's avatar

Author: Stefan Henneken

I’m Stefan Henneken, a software developer based in Germany. This blog is just a collection of various articles I want to share, mostly related to Software Development.

Leave a comment