1. Overview: Why STL Must Be Rewritten for the S7-1200
Statement List (STL) is part of the IEC 61131-3 text language family and was the dominant Siemens programming notation on the S7-300 and S7-400 generations. The S7-1200 controller family, however, has a fundamentally different firmware architecture than the S7-300/400 and historically only supported Ladder Diagram (LAD), Function Block Diagram (FBD), and Structured Control Language (SCL). The S7-1500 reintroduced limited STL support starting with TIA Portal V15.1 firmware 2.0, but STL is not available on any S7-1200 CPU firmware release. Any program originally written in STL for an S7-300/400 must therefore be translated into LAD, FBD, or SCL before it can run on an S7-1200.
SCL is the recommended target language for this kind of migration because it preserves the algorithmic clarity of the original STL, executes deterministically inside the standard OB1 cycle (or any user OB), and exposes data-block word addresses (DBW) as named tags. This article walks through the complete migration of a real STL routine that performs a conditional three-word comparison and a small arithmetic post-processing step, then verifies the result against a soft-PLC simulation.
2. Prerequisites
| Item | Requirement |
|---|---|
| Engineering software | TIA Portal V16 or later (V17/V18 recommended for current S7-1200 firmware support) |
| Controller family | SIMATIC S7-1200, CPU firmware 4.2 or later for full SCL feature set |
| Source program | Validated STL source from S7-300 / S7-400 project (STEP 7 V5.x or TIA Portal V13/V14 STL block) |
| Hardware identifier | Defined DB number (e.g., DB100) holding the input/output words used in the original code |
| Library knowledge | Standard IEC 61131-3 operators: arithmetic, comparison, bit-shift |
3. Decoding the Original STL Routine
The source code is a single STL network that performs two cascaded conditional subtracts, a recompute pass, an averaging step, an addition, counter settings, and a bit-reset sequence. Before any translation begins, the algorithm must be understood mathematically so the SCL version produces identical results for every possible input combination.
3.1 Word and Bit Inventory
| Address | Role in algorithm | Suggested SCL tag |
|---|---|---|
| DBW 0 | Control/status word 1 | iCtrl1 : INT |
| DBW 2 | Control/status word 2 | iCtrl2 : INT |
| DBW 4 | Compare value A | iValA : INT |
| DBW 6 | Compare value B | iValB : INT |
| DBW 8 | Compare value C | iValC : INT |
| DBW 10 | Reference threshold 1 | iRef1 : INT |
| DBW 12 | Reference threshold 2 | iRef2 : INT |
| DBW 14 | Computed sum | iSum : INT |
| DBW 16 | Half-sum (shift right 1) | iHalf : INT |
| DBW 20 | Offset to add | iOffset : INT |
| DBW 24 | Final result | iResult : INT |
| M 0.3 | Status flag 1 | bFlag1 : BOOL |
| M 2.0 to 2.5 | Status flags 2..7 | bFlag2..bFlag7 : BOOL |
| C 9, C 10, C 20, C 21 | IEC counters | CT9, CT10, CT20, CT21 |
3.2 Branching Model Translated from Jump Instructions
STL uses JM (jump if negative/CC1=1), JP (jump if positive/CC1=0 and CC0=0), and JU (unconditional jump). On the S7-300/400 these instructions inspect the condition-code bits set by the immediately preceding -I or +I. The same logic can be expressed with relational operators in SCL.
| STL pattern | Meaning | SCL equivalent |
|---|---|---|
L DBW x; L DBW y; -I; JM label |
If (x - y) < 0 then jump | IF (x - y) < 0 THEN ... END_IF; |
L DBW x; L DBW y; -I; JP label |
If (x - y) > 0 then jump | IF (x - y) > 0 THEN ... END_IF; |
JU label |
Unconditional jump | Code flows to next statement |
NOP 0 |
Label target / no-op | Comments or label constant |
3.3 Reverse-Engineered Algorithm
Tracing the four JU paths and the two independent -I decision trees, the routine reduces to:
- Compute
d1 = iValA - iValBand classify the sign. - If
d1 < 0, compared1againstiRef1and route to either M003 (sum branch) or M004 (bypass branch). - If
d1 > 0, compared1againstiRef2and again route to M003 or M004. - If
d1 == 0, always go to M003. - At M003, perform a second classification using
iValA - iValC, with the same two threshold comparisons. The logic keeps the result sum-conditional based on a second internal state. - After classification, unconditionally compute
iSum = iValA + iValB + iValC,iHalf = iSum SHR 1(divide by 2), andiResult = iHalf + iOffset. - Set counters C9, C10 with PV from DBW0; set counters C20, C21 with PV from DBW2; reset M0.3 and the six bits M2.0 to M2.5.
- Fall through to label M004 (NOP 0), which is the end-of-network marker.
Note that the original author re-uses the accumulator: the first -I leaves the difference in ACCU1, and the second subtract inside M001 and M002 therefore operates on the difference, not on a freshly loaded value. This is critical for bit-exact behavior.
4. SCL Translation Patterns
Three reusable patterns cover 90 percent of STL-to-SCL conversion on the S7-1200.
4.1 Jump-to-IF Pattern
// Original STL
L DBW 4
L DBW 6
-I
JM M001
JP M002
JU M003
// SCL equivalent
IF (iValA - iValB) < 0 THEN
// M001 body
ELSIF (iValA - iValB) > 0 THEN
// M002 body
ELSE
// M003 body
END_IF;
4.2 Cascaded-Decision Pattern
// When the second subtract inherits the ACCU1 from the first
IF d1 < 0 THEN
d2 := d1 - iRef1;
IF d2 < 0 THEN GOTO_SUM := FALSE; ELSE GOTO_SUM := TRUE; END_IF;
ELSIF d1 > 0 THEN
d2 := d1 - iRef2;
IF d2 > 0 THEN GOTO_SUM := FALSE; ELSE GOTO_SUM := TRUE; END_IF;
ELSE
GOTO_SUM := TRUE;
END_IF;
4.3 Unconditional-Fall-Through Pattern
An unconditional JU in STL becomes a missing ELSIF/ELSE branch in SCL. The body that follows the JU in STL is the body of the ELSE in SCL.
5. Complete SCL Implementation for the S7-1200
The following code is a single SCL FB or FC block. Replace DB100 with the data block number used in the original project. All tags are declared locally for clarity but can be moved to the DB without code changes.
FUNCTION_BLOCK "FB_SortPostProc"
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
VAR
iCtrl1 : INT; // DBW 0
iCtrl2 : INT; // DBW 2
iValA : INT; // DBW 4
iValB : INT; // DBW 6
iValC : INT; // DBW 8
iRef1 : INT; // DBW 10
iRef2 : INT; // DBW 12
iSum : INT; // DBW 14
iHalf : INT; // DBW 16
iOffset : INT; // DBW 20
iResult : INT; // DBW 24
bFlag1 : BOOL; // M0.3
bFlag2 : BOOL; // M2.0
bFlag3 : BOOL; // M2.1
bFlag4 : BOOL; // M2.2
bFlag5 : BOOL; // M2.3
bFlag6 : BOOL; // M2.4
bFlag7 : BOOL; // M2.5
END_VAR
VAR_TEMP
d1 : INT;
d2 : INT;
bDoSum : BOOL;
END_VAR
BEGIN
// ---------- Stage 1: classify (iValA - iValB) ----------
d1 := iValA - iValB;
bDoSum := FALSE;
IF d1 < 0 THEN
// M001 body: re-use d1 as ACCU1
d2 := d1 - iRef1;
IF d2 < 0 THEN
// jump to M004 (bypass)
bDoSum := FALSE;
ELSE
// jump to M003 (sum)
bDoSum := TRUE;
END_IF;
ELSIF d1 > 0 THEN
// M002 body: re-use d1 as ACCU1
d2 := d1 - iRef2;
IF d2 > 0 THEN
// jump to M004 (bypass)
bDoSum := FALSE;
ELSE
// jump to M003 (sum)
bDoSum := TRUE;
END_IF;
ELSE
// M003 reached from JU (equal case)
bDoSum := TRUE;
END_IF;
// ---------- Stage 2: second-pass classification ----------
IF bDoSum THEN
d1 := iValA - iValC;
IF d1 < 0 THEN
d2 := d1 - iRef1;
IF d2 < 0 THEN bDoSum := FALSE; END_IF;
ELSIF d1 > 0 THEN
d2 := d1 - iRef2;
IF d2 > 0 THEN bDoSum := FALSE; END_IF;
END_IF;
END_IF;
// ---------- Stage 3: arithmetic post-processing ----------
IF bDoSum THEN
iSum := iValA + iValB + iValC;
iHalf := iSum SHR 1; // integer divide by 2
iResult := iHalf + iOffset;
END_IF;
// ---------- Stage 4: counter and bit handling ----------
// STL "S C9" with PV from ACCU1 (here DBW 0) is emulated
// with the IEC counter instructions below.
"CT9"(CU := TRUE, R := FALSE, PV := iCtrl1);
"CT10"(CU := TRUE, R := FALSE, PV := iCtrl1);
"CT20"(CU := TRUE, R := FALSE, PV := iCtrl2);
"CT21"(CU := TRUE, R := FALSE, PV := iCtrl2);
bFlag1 := FALSE;
bFlag2 := FALSE;
bFlag3 := FALSE;
bFlag4 := FALSE;
bFlag5 := FALSE;
bFlag6 := FALSE;
bFlag7 := FALSE;
// ---------- Label M004 (NOP 0) ----------
// Implicit end of network; no operation required.
END_FUNCTION_BLOCK
6. Data Block Layout and Symbol Mapping
Because the original code uses absolute addresses such as DBW 4, the migrated block benefits from symbolic naming. TIA Portal's Rewire function can replace the old DBW references with new symbolic ones in a single pass.
| STL address | SCL tag | Data type | Default range |
|---|---|---|---|
| DBW 0 | iCtrl1 | INT | -32768 to 32767 |
| DBW 2 | iCtrl2 | INT | -32768 to 32767 |
| DBW 4 | iValA | INT | -32768 to 32767 |
| DBW 6 | iValB | INT | -32768 to 32767 |
| DBW 8 | iValC | INT | -32768 to 32767 |
| DBW 10 | iRef1 | INT | application-specific |
| DBW 12 | iRef2 | INT | application-specific |
| DBW 14 | iSum | INT | computed |
| DBW 16 | iHalf | INT | computed |
| DBW 20 | iOffset | INT | application-specific |
| DBW 24 | iResult | INT | computed |
SHR 1 instruction on a 16-bit INT performs an arithmetic/logical right shift by one bit. The sign bit is preserved only for WORD; for INT the operation is logical, so a value of -3 (0xFFFD) becomes 0x7FFE = 32766. Verify the sign-handling expectation against the original STL by stepping the code in the S7-PLCSIM simulator and comparing iHalf against the legacy control.7. Edge Cases and Boundary Behavior
| Scenario | Expected result | Verification |
|---|---|---|
| iValA == iValB, any iValC | bDoSum = TRUE; iResult = (3 * iValA) / 2 + iOffset | Test with iValA = 100, iValB = 100, iValC = -50 |
| iValA < iValB and d1 < iRef1 | bDoSum = FALSE; iResult unchanged | Test with iValA = 10, iValB = 100, iRef1 = 200 |
| iValA > iValB and d1 > iRef2 | bDoSum = FALSE; iResult unchanged | Test with iValA = 500, iValB = 100, iRef2 = 200 |
| INT overflow in iSum | Wrap-around at 32767/-32768 | Test with iValA = 20000, iValB = 20000, iValC = 20000 |
| All thresholds and values at INT_MIN/MAX | Undefined intermediate; ALU flags behave as documented | Run SCL overflow test suite from S7-PLCSIM |
| Counter PV at zero | Counter remains at zero (Q = FALSE) | Set iCtrl1 = 0, iCtrl2 = 0; monitor Q outputs |
8. Counter and Bit-Reset Operations
The original STL sets four IEC counters from accumulator values (S C9 means "set counter with PV = ACCU1"). In SCL, the IEC counter faceplate is the cleanest representation. For an S7-1200 the IEC counters are accessible as system function blocks: CTU (count up), CTD (count down), and CTUD (count up/down).
// Background DB instance for the CTU instructions
"CT9"(CU := bRun, R := bReset, PV := iCtrl1, Q => bCT9_Q, CV => iCT9_CV);
"CT10"(CU := bRun, R := bReset, PV := iCtrl1);
"CT20"(CU := bRun, R := bReset, PV := iCtrl2);
"CT21"(CU := bRun, R := bReset, PV := iCtrl2);
If the original project used SIMATIC counters (S C9, S C10) the equivalent on the S7-1200 is the IEC CTU with an instance DB. The S7-1200 has a fixed, limited number of IEC counter resources per CPU; refer to the relevant CPU manual (for example the S7-1200 System Manual) for the exact maximum number of CTU/CTD/CTUD blocks that can be instantiated.
9. Migration Checklist
- Open the legacy STEP 7 V5.x project and export the STL source as a text/printable file.
- Create a new TIA Portal project and add an S7-1200 station with the correct CPU order number (for example 6ES7214-1AG40-0XB0).
- Create a global DB named SortData with INT and BOOL tags as per Section 6.
- Insert a new SCL block (FB1) and paste the translated source from Section 5.
- Compile the block. Resolve any implicit-conversion warnings (for example BOOL to INT comparisons).
- Call FB1 from OB1 with an instance DB.
- Use TIA Portal's Rewire function to map the old DBW addresses to the new symbolic tags.
- Download the project to the CPU and run an online watch table on the new DB.
10. Verification and Commissioning
Run the SCL block inside S7-PLCSIM and exercise the boundary conditions from Section 7. The verification table below defines the comparison method for each input class.
| Test class | Inputs (A, B, C, Ref1, Ref2, Offset) | Expected iResult | Pass criterion |
|---|---|---|---|
| All-positive, sum branch | 100, 200, 300, 0, 0, 10 | (100+200+300)/2 + 10 = 310 | iResult == 310 |
| Bypass branch | 10, 500, 800, 0, 50, 0 | Unchanged (last value) | iResult == previous iResult |
| Negative mix | -50, -50, 100, 0, 0, 5 | (-50 + -50 + 100)/2 + 5 = 5 | iResult == 5 |
| INT overflow | 30000, 30000, 30000, 0, 0, 0 | Wrap to -4768 (0xED30) | iResult == -4768 (document as platform behavior) |
| Counter ramp | iCtrl1 = 5, pulse CU 10 times | CT9.CV = 5, Q = TRUE after 5th pulse | Watch table shows Q=1, CV=5 |
| Bit reset | Pre-set M2.0..M2.5 to TRUE, run OB1 | All M2.x become FALSE | Watch table shows FALSE for all |
11. Field-Proven Caveats
- STL is gone, not hidden. TIA Portal V16-V18 do not expose an STL editor for the S7-1200. The block must be a new SCL source. Do not attempt to paste STL text into a SCL editor; the parser will reject it.
- Optimized vs. non-optimized DB. If the SCL block is configured with optimized access, absolute DBW addresses cannot be used inside the block. Either set the DB to non-optimized for direct compatibility, or use the fully symbolic tag interface shown in Section 5.
- Counter resource budget. Each S7-1200 CPU has a documented maximum for IEC counter and timer blocks. The four CTU instances in this example are well within the budget for any modern CPU, but a project that re-uses this pattern in many FBs may exhaust the IEC counter pool. The remedy is to use the IEC_Timer/IEC_Counter instances in a global DB pool.
-
Shift semantics on INT. The original
SRW 1is defined for the WORD view of the accumulator. In SCL theSHRoperator on an INT performs a logical shift; for negative values this differs from the original two's-complement arithmetic shift. If sign preservation is required, cast toWORDfirst, shift, then cast back:iHalf := WORD_TO_INT(SHR(INT_TO_WORD(iSum), 1)); -
Accumulator reuse. STL reuses ACCU1 across
-Iinstructions without an explicit reload. The SCL translation must re-bind the previous result to a temporary variable, otherwise the second subtract will load a fresh value from the data block and break the bit-exact equivalence. - Watchdog time. The SCL version is on average 1.3 to 2.0 times slower than the equivalent STL on the same cycle. For a one-shot routine that runs once per OB1 this is irrelevant. For routines in fast OBs (OB35, OB82) profile the cycle in TIA Portal's Online & Diagnostics > Cycle Time view before commissioning.
12. FAQ
Can the S7-1200 execute STL at all?
No. STL is not available on any S7-1200 CPU firmware release. S7-1500 CPUs starting at firmware V2.0 with TIA Portal V15.1 or later have limited STL support. S7-1200 supports LAD, FBD, and SCL only.
What is the difference between SRW 1 and SHR 1 in SCL?
SRW 1 in STL shifts the 16-bit accumulator right by one bit as a WORD, preserving the sign bit only because the WORD type is unsigned. In SCL, the SHR operator on an INT performs a logical shift that does not preserve the sign for negative values. Cast through WORD if sign-preserving arithmetic shift is required.
How do I emulate the STL S C9 instruction in SCL?
Use the IEC CTU (count up) system function block: "CT9"(CU := bRun, R := bReset, PV := iCtrl1, Q => bCT9_Q, CV => iCT9_CV); The PV is loaded from iCtrl1 (originally DBW 0). The instance DB is generated automatically by TIA Portal when the CTU is placed.
Does the translation change the cycle time of OB1?
Yes. SCL executes the same algorithm in roughly 1.3 to 2.0 times the STL cycle, because each IF/ELSIF compiles to a sequence of load-and-compare operations rather than direct ALU flag checks. For most applications this is well under the OB1 watchdog of 150 ms (default) on the S7-1200.
Can I keep the original DBW absolute addresses inside the SCL block?
Yes, but only if the data block is set to non-optimized access. With optimized access, TIA Portal does not allow absolute DBW references inside SCL. The recommended approach is to declare symbolic tags in the block interface or in a non-optimized DB, then use the tag names in SCL.
How do I verify the migration is bit-exact?
Run the legacy STL block and the new SCL block in parallel inside S7-PLCSIM with the same input vectors, then compare iResult, iHalf, and the bit flags using a watch table. Any divergence indicates a missing accumulator reload or a sign-handling mismatch in the SHR operator.