Siemens SCL Programming: S7-300/400/1200/1500 Tutorial Guide

David Krause13 min read
SiemensTIA PortalTutorial / How-to
Licensed PE Working through this on a live machine? A Maine-licensed engineer can take it from here — included with IMD hardware, by the hour for everything else. Book an engineer

Siemens SCL Programming: S7-300/400/1200/1500 Tutorial Guide

1. Overview

Structured Control Language (SCL) is Siemens' high-level, PASCAL-derived text language for S7 programmable logic controllers. It conforms to the IEC 61131-3 standard (ST - Structured Text) and is supported on the S7-300, S7-400, S7-1200, S7-1500, WinAC, and ET 200SP CPU families. SCL ships as a standalone option package with STEP 7 V5.x and as a fully integrated editor in TIA Portal starting with V11.

Use SCL when ladder logic becomes too dense, when you need complex math, data handling, loops, or string processing. SCL is compiled to MC7 (S7-300/400) or to the SCL runtime in TIA Portal - it does not interpret at run time, so execution is comparable to STL in performance on the same CPU.

2. Prerequisites

  • STEP 7 V5.5 SPx (for S7-300/400) with the SCL optional package installed, or TIA Portal V13 or later (V15.1, V16, V17, V18, V19, V20, or V21 for current CPUs).
  • A Siemens license for the SCL compiler. On STEP 7 V5.x the MLFB is 6ES7 811-1CC04-0YA5 (SCL V5.3 for S7-300/400). On TIA Portal, SCL is included in the STEP 7 Professional license.
  • Familiarity with PLC basics: tags, I/O mapping, OB/FB/FC/DB organization, and the S7 data model.
  • A programming device with Windows 10/11 (TIA Portal V16+) or Windows 7/10 (STEP 7 V5.5).
License note: Without a valid SCL license, the editor opens in "demo mode" - you can edit but blocks will not compile to a downloadable program. Demo blocks are flagged with a yellow comment in the compiled SCL source.

3. SCL Project Structure and Block Types

SCL can be written inside the following block types. All blocks are stored in the S7 program under Program blocks:

Block type File extension Use
OB (Organization Block) .db / .ob Cyclic, startup, time-of-day, hardware interrupt, error OB
FC (Function) .fc Stateless subroutine with a return value
FB (Function Block) .fb Stateful block with instance DB
DB (Data Block) .db Data storage - SCL edits code-style DBs
UDT (User-Defined Type) .udt Reusable type/structure declaration

4. Data Types and Declarations

SCL uses the same elementary and complex data types defined in IEC 61131-3. All tags must be declared in the block's Declaration section, with the syntax:

Name : Type [:= InitialValue];

4.1 Elementary Types

Type Size Range
BOOL 1 bit FALSE / TRUE (0, 1)
SINT 8 bit -128 to 127
INT 16 bit -32 768 to 32 767
DINT 32 bit -2 147 483 648 to 2 147 483 647
LINT 64 bit -9.22e18 to 9.22e18 (S7-1500)
USINT 8 bit 0 to 255
UINT 16 bit 0 to 65 535
UDINT 32 bit 0 to 4 294 967 295
REAL 32 bit IEEE 754 single precision
LREAL 64 bit IEEE 754 double precision (S7-1500)
TIME 32 bit T#-24d20h31m23s648ms to T#24d20h31m23s647ms
DATE 16 bit D#1990-01-01 to D#2168-12-31
TOD 32 bit TOD#00:00:00.000 to TOD#23:59:59.999
DT / DTL 64 bit Date and time, BCD or structure (DTL is S7-1500)
STRING[n] n+2 byte Up to 254 characters (n = 0 to 254)
WSTRING[n] n*2+4 byte Unicode, S7-1500 only
CHAR / WCHAR 1 / 2 byte Single character

4.2 Complex Types


TYPE MotorData :
  STRUCT
    Speed      : REAL := 0.0;
    Current    : REAL := 0.0;
    RunHours   : UDINT := 0;
    State      : BOOL  := FALSE;
  END_STRUCT;
END_TYPE

Arrays use a comma-separated range. Multi-dimensional arrays are supported up to six dimensions on S7-1500, three on S7-300/400.


VAR
  Conveyors : ARRAY[1..10] OF BOOL;
  Recipe    : ARRAY[1..5, 1..3] OF REAL;
END_VAR

4.3 Declaration Sections

Section Storage Use
VAR INPUT Block input Parameters passed in by reference (IN)
VAR_OUTPUT Block output Returned values (OUT)
VAR_IN_OUT In/out parameter Caller passes a tag; block can read and write
VAR_TEMP Local stack Temporary tags; reset every call
VAR STATIC Instance DB (FB only) Retained between calls
VAR CONSTANT Compile-time Read-only named constant
VAR_GLOBAL Global DB (OB1 / DB only) Shared across blocks

5. Operators

Class Operators Precedence
Parentheses ( ) 1 (highest)
Function call FCName( ) 2
Power ** 3
Unary -, NOT 4
Multiplicative *, /, MOD, DIV 5
Additive +, - 6
Comparison <, <=, >, >=, =, <> 7
Equality =, <> 8
Boolean AND AND, & 9
Boolean XOR XOR 10
Boolean OR OR 11 (lowest)

The integer division operator DIV returns the integer quotient, MOD returns the remainder. Mixing REAL and INT in an expression is allowed; the integer is implicitly cast to REAL. Use explicit conversion functions REAL_TO_INT, INT_TO_REAL, WORD_TO_INT, DWORD_TO_REAL, etc., when crossing type families.

6. Control Structures

6.1 IF...THEN...ELSIF...ELSE...END_IF


IF iMotorCurrent > 50.0 THEN
  bOverload := TRUE;
  iAlarmCode := 16#0001;
ELSIF iMotorCurrent > 40.0 THEN
  bWarning := TRUE;
  iAlarmCode := 16#0002;
ELSE
  bOverload := FALSE;
  bWarning  := FALSE;
  iAlarmCode := 0;
END_IF;

6.2 CASE...OF


CASE iState OF
  0:  sStateText := 'IDLE';
  1:  sStateText := 'STARTING';
  2:  sStateText := 'RUNNING';
  10..19:
       sStateText := 'FAULT';
       iSubState  := iState - 10;
  ELSE
       sStateText := 'UNKNOWN';
END_CASE;

6.3 Loops


// FOR with fixed bounds (compiler unrolls where possible)
FOR i := 1 TO 100 BY 2 DO
  Sum := Sum + i;
END_FOR;

// WHILE - condition evaluated first
WHILE bRunning AND NOT bError DO
  iCounter := iCounter + 1;
  IF iCounter > 10000 THEN
    EXIT;  // leave the loop early
  END_IF;
END_WHILE;

// REPEAT - body executed at least once
REPEAT
  rAverage := (rMin + rMax) / 2.0;
  IF rAverage * rAverage > rTarget THEN
    rMax := rAverage;
  ELSE
    rMin := rAverage;
  END_IF;
UNTIL ABS(rMax - rMin) < 0.001
END_REPEAT;
Loop safety: SCL loops on S7-300/400 share a single 16-bit local stack. Deeply nested loops or large FOR ranges can overflow the temp area. Use EXIT to break out and reduce nesting. On S7-1500 the local stack is virtually unlimited.

7. Functions and Function Blocks

7.1 FC with Return Value


FUNCTION FC_Limit : REAL
VAR_INPUT
  rValue    : REAL;
  rLow      : REAL;
  rHigh     : REAL;
END_VAR
BEGIN
  IF rValue < rLow  THEN FC_Limit := rLow;  RETURN; END_IF;
  IF rValue > rHigh THEN FC_Limit := rHigh; RETURN; END_IF;
  FC_Limit := rValue;
END_FUNCTION

7.2 FB with Instance Data


FUNCTION_BLOCK FB_PID
VAR_INPUT
  rSetpoint  : REAL;
  rProcess   : REAL;
  bEnable    : BOOL;
END_VAR
VAR_OUTPUT
  rOutput    : REAL;
  bActive    : BOOL;
END_VAR
VAR
  rIntegral  : REAL;     // persistent
  rLastError : REAL;
  Kp : REAL := 1.0;
  Ki : REAL := 0.1;
  Kd : REAL := 0.0;
END_VAR
BEGIN
  IF bEnable THEN
    rError := rSetpoint - rProcess;
    rIntegral := rIntegral + rError * CycleTime;
    rOutput := Kp * rError + Ki * rIntegral + Kd * (rError - rLastError);
    rLastError := rError;
    bActive := TRUE;
  ELSE
    rOutput := 0.0;
    bActive := FALSE;
  END_IF;
END_FUNCTION_BLOCK

Calling the FB in OB1 with a multi-instance DB:


VAR
  TempCtrl : FB_PID;
END_VAR

TempCtrl(rSetpoint := 80.0, rProcess := aiTemp, bEnable := TRUE,
         rOutput => aoHeater, bActive => bHeaterOn);

7.3 Multi-Instance and Parameter Passing

On S7-1200/1500, parameters can be passed by value (INPUT) or by reference (IN_OUT, OUTPUT). Use := for default values in declarations, and the named-parameter style (param := value, param2 => out) to avoid order mistakes.

8. Working with Strings and Arrays

8.1 String Functions


VAR
  sSrc  : STRING[80] := 'Conveyor 3 fault';
  sFind : STRING[6]  := 'fault';
  iPos  : INT;
END_VAR

iPos := FIND(IN1 := sSrc, IN2 := sFind);     // returns 11 or 0
IF iPos > 0 THEN
  sSrc := INSERT(sSrc, '_ACK', LEN(sSrc) - iPos - LEN(sFind) + 1);
END_IF;

8.2 ARRAY Index Validation


FOR i := 1 TO 32 DO
  IF aInputs[i] THEN                         // standard indexing
    iCount := iCount + 1;
  END_IF;
END_FOR;

On S7-1200/1500, ARRAY index checks are runtime-enforced; an out-of-range access produces SF LED + diagnostic buffer entry "Area length error". Wrap accesses in a IF (i >= LOWER_BOUND(a) AND i <= UPPER_BOUND(a)) block when the index comes from a field device.

9. SCL Editor Workflow in TIA Portal

  1. Open the project in TIA Portal V16+ and select a CPU in the project tree.
  2. Expand Program blocks and double-click Add new block.
  3. Pick the block type (FB/FC/OB) and set language to SCL.
  4. Type declarations in the upper declaration table; write code in the lower editor.
  5. Press F7 to compile. Errors appear in the Inspector under Compile / Syntax check.
  6. Drag the block into OB1 (or a higher-level FB) to instantiate it. Wire I/O via the LAD/FBD or SCL interface.
  7. Download to the CPU and use Online & diagnostics -> Monitor / modify to step through SCL.

10. Comparison: SCL vs. LAD/FBD/STL

Feature SCL LAD FBD STL (S7-300/400)
High-level constructs (IF, FOR, WHILE) Yes Limited Limited Yes
Code density High Low Low High
String/array operations Native Manual Manual Manual
Standardization IEC 61131-3 ST IEC 61131-3 LD IEC 61131-3 FBD Siemens-specific
Reusability Functions, libraries FC/FB FC/FB FC/FB
Performance Compiled, near-native Interpreted in S7-300/400, compiled S7-1500 Same as LAD Fastest, direct MC7
Learning curve PASCAL background Electrical schematic Logic-gate Assembly-like
Debug visibility Single-step, breakpoints Network monitor Network monitor Single-step, accumulator view

On S7-1500, LAD and FBD are compiled to native code by the TIA Portal compiler, so the runtime gap with SCL is small. On S7-300/400, LAD/FBD is interpreted while SCL is compiled - SCL can be 2-10x faster for complex math.

11. Library and Reuse Patterns

Build a master SCL library once and reuse it across machines:

  • Global libraries in TIA Portal: File > New Library > Global Library. Add typed FBs and UDTs. Use versioned library updates to roll out bug fixes.
  • Know-how protection: right-click the block, select Properties > General > Protection. Apply a password so that source code in the SCL editor is hidden during online operations.
  • Source-file import/export: export blocks as .scl text. This enables git-based version control and side-by-side diffs.

12. SCL for Data Handling and Recipes

Recipes are a common SCL use case. The example stores 20 parameters per recipe, with 8 recipes in a DB:


TYPE RecipeRow :
  STRUCT
    Name     : STRING[16];
    Setpoint : REAL;
    Tolerance: REAL;
    RampTime : TIME;
    Enabled  : BOOL;
  END_STRUCT;
END_TYPE

DATA_BLOCK DB_Recipes
  STRUCT
    Active : INT := 0;
    List   : ARRAY[1..8] OF RecipeRow;
  END_STRUCT
END_DATA_BLOCK

FUNCTION FC_LoadRecipe : BOOL
VAR_INPUT
  iNumber : INT;
END_VAR
BEGIN
  IF iNumber < 1 OR iNumber > 8 THEN
    FC_LoadRecipe := FALSE;
    RETURN;
  END_IF;
  DB_Recipes.Active := iNumber;
  rSetpoint := DB_Recipes.List[iNumber].Setpoint;
  tRamp     := DB_Recipes.List[iNumber].RampTime;
  FC_LoadRecipe := TRUE;
END_FUNCTION

13. Diagnostics and Error Handling

Use the EN/ENO mechanism (ladder-style) only when calling SCL from LAD. Inside SCL, prefer explicit error variables:


IF iNumber < LOWER_BOUND(DB_Recipes.List, 1)
   OR iNumber > UPPER_BOUND(DB_Recipes.List, 1) THEN
  bError := TRUE;
  iStatus := 16#8001;     // custom code
  RETURN;
END_IF;

For CPU-side error reporting, raise a system function:


IF bFatalFault THEN
  RALRM(OB_NUM := 82, OL := FALSE, F_ID := 16#0001);  // generate diagnostic interrupt
END_IF;

14. Best Practices

  1. One block, one function. Keep FCs short (< 200 lines). If larger, split into helpers.
  2. Avoid magic numbers. Use VAR CONSTANT or DB constants for setpoints, limits, and timing.
  3. Bound every loop. Use UPPER_BOUND / LOWER_BOUND macros - never write hard-coded loop limits that differ from the array size.
  4. Use named parameters at call sites. Easier to read and unaffected by interface re-ordering.
  5. Initialize outputs. Set every VAR_OUTPUT at the start of the block to a known value, so the caller never sees undefined data if the block returns early.
  6. Keep VAR_IN_OUT minimal. It blocks the optimizer and prevents read-only access.
  7. Version your libraries. Tag with SemVer in the library comment and use TIA Portal's library update path.
  8. Compile clean, every time. Warnings become errors after firmware upgrades. Treat them seriously.

15. Verification and Commissioning

Before downloading an SCL block to a running machine:

  1. Compile with all warnings enabled (Project > Properties > Compile).
  2. Cross-check interface changes in Compile / Cross-reference - confirm no consumer was missed.
  3. Use the SCL PLCSIM instance: load the project into S7-PLCSIM V16+ and exercise the FC/FB with the Watch table.
  4. Use breakpoints in the SCL editor (right margin - red dot). Single-step with F8, watch live tag values. Breakpoints require a SCL-compatible CPU firmware (S7-1500 since V1.5; S7-1200 since V4.0; S7-300/400 with breakpoints disabled by default - activate via Online > Breakpoint properties).
  5. For S7-1500, run the Inspector > Compile messages tab and confirm zero warnings.

16. Common Errors and Fixes

Symptom Cause Fix
Compiler: "Identifier already declared" Duplicate tag name in same scope Rename, check for accidental section duplication
Compiler: "Type mismatch" INT assigned to REAL without cast Use INT_TO_REAL() / REAL_TO_INT()
CPU SF LED: "Area length error" ARRAY index out of range Validate index with LOWER_BOUND/UPPER_BOUND
CPU SF LED: "Division by zero" Divisor is 0 Guard with IF divisor <> 0
Block is yellow in compiled program No SCL license Install license via Automation License Manager
Source download fails CPU firmware older than library's target firmware Update CPU firmware, or use compatibility mode
Online monitor: variables show "invalid" Instance DB not downloaded, or watch table points to wrong area Download blocks including all referenced DBs

17. Performance Tips

  • Use DINT/REAL instead of INT on S7-1500 - the SCL runtime prefers 32-bit native operations.
  • Replace division with multiplication by reciprocal when a constant divisor is used heavily.
  • Avoid VAR_IN_OUT for read-only parameters - use VAR_INPUT; the compiler can copy the value into a register and skip a writeback.
  • On S7-1500, declare large temporary arrays as VAR_TEMP only in FBs that get a new instance DB; otherwise they consume the L-stack of the calling OB.

18. SCL Reference Documentation

Authoritative Siemens documentation for SCL:

  • Siemens Online Help "S7-300/400: Structured Control Language (SCL) for S7-300/S7-400 Programming" - SCL manual (PDF)
  • TIA Portal Online Help "Creating SCL programs / Basics of SCL" - TIA Portal SCL (V21)
  • STEP 7 Professional / TIA Portal installation manual - lists the SCL optional package and license order numbers.
  • IEC 61131-3:2013 - international standard defining the ST language, SCL implements.
The 4th-edition SCL PDF referenced above covers STEP 7 V5.x syntax and is the canonical reference for S7-300/400. For TIA Portal V13 and later, prefer the integrated help, which is updated every release and is the most current.

19. Suggested Learning Path

  1. Read the first 4 chapters of the SCL PDF (overview, language description, data types, expressions).
  2. Build the IF / CASE / FOR examples in PLCSIM.
  3. Port a small LAD project to SCL - convert one FC at a time and benchmark.
  4. Write a UDT for a motor and an FB that exposes only named parameters.
  5. Package the FBs into a global library and import them into a second project.

20. FAQ

Is SCL the same as IEC 61131-3 Structured Text?

SCL is Siemens' implementation of the IEC 61131-3 ST language. Most ST constructs work identically, but Siemens adds extensions such as EN/ENO behavior, direct access to I/Q/M operands, and block-aware parameter syntax (INPUT/IN_OUT/OUTPUT/RETURN). 100% ST code from another vendor will usually compile after minor edits to address formats.

Which Siemens CPUs support SCL?

S7-300 (CPU 313 and above), S7-400, WinAC, S7-1200 (firmware 4.0+), S7-1500 (all firmware versions), and ET 200SP CPUs. S7-200 does not support SCL. The S7-1500 also supports the SCL "extended" subset with LREAL, LINT, and WSTRING.

Do I need a separate license to use SCL in TIA Portal?

Yes. SCL is part of the STEP 7 Professional license package. With STEP 7 Basic, the SCL blocks are still editable but cannot be compiled to a runnable program. Use the Automation License Manager to install the license on the engineering station and transfer it to the CPU's SIMATIC memory card if required.

Can I mix SCL and LAD in the same block?

No - each block is a single language. But you can call an SCL FC from a LAD network and vice versa. A common pattern is LAD for I/O mapping and safety logic, SCL for math, data, and recipe handling.

Where can I find SCL sample projects?

Open the TIA Portal "Add new block" dialog, click the Examples tab - it ships with simple SCL FCs and FBs. For S7-300/400, the "SCL V4" PDF linked above contains ready-to-paste example code for IF, CASE, FOR, WHILE, and STRING functions.

Back to blog