Problem Summary
A STEP 7 V5.5 STL function block copied verbatim into a TIA Portal project targeting an ET 200S IM151-8 PN/DP (or any S7-1200/S7-1500 CPU) fails to compile, downloads with warnings, or silently writes zeros at runtime. The reported failure is on the local variable area: statements such as A L 1.0, = L 3.1, L PIW [AR1, P#10.0], and T PQW [AR1, P#8.0] are accepted by the S7-300/400 STL compiler but rejected or semantically changed on the newer targets. The block drives a PROFIdrive slave through the PKW/PZD channel (parameter channel PCV_PCA, PCV_IND, Data) and uses the local temp area as a scratch register while assembling the control word.
The visible symptom in the user's log is one of the following:
- "The address area 'L' does not exist in this CPU type."
- "STL is not supported on the selected CPU."
- The block compiles, but
nOutState,fDrvRdy, andValor_Refstay at zero, the drive never leaves ramp-up, andFALHA_MONlatches because no control-word toggle is seen. - Compiler emits a warning that
AR1,AR2, and DB registers are being used in a way that is not guaranteed safe for the optimization level selected on the S7-1500.
This article gives the exact root cause for each symptom and a reproducible migration path that preserves the drive interface while moving the code to a target TIA Portal can compile against.
Root Cause Analysis
There are four overlapping root causes, all originating from the same migration mistake: the user assumed that copying STL source from STEP 7 V5.5 into a TIA Portal FB keeps the semantics of the original block. It does not, because:
-
STL language support is hardware-dependent. S7-1200 firmware does not implement STL at all. S7-1500 implements STL since firmware V1.5 (with restrictions: indirect memory access via
AR1/AR2and area-crossing pointer arithmetic are rejected). The ET 200S IM151-8 PN/DP (6ES7151-8AB01-0AB0) compiles as an S7-1500-class CPU and behaves like an S7-1500 regarding STL restrictions. -
The local data area layout (
Lstack) is implementation-defined. On S7-300/400, the compiler allocates 16-bit local data words and the programmer addresses them by byte/bit offset (L 1.0= local data byte 1, bit 0;L 3.2= byte 3, bit 2). On S7-1500 the compiler is free to reorder locals for optimization; only the symbolic name is guaranteed. The bit-slice access on the literalLstack is removed. -
The pointer-arithmetic pattern
SLW 3 / LAR1followed byPIW [AR1, P#10.0]builds an area-internal byte pointer from the logical address#IW_INIC. This is a deprecated access pattern on S7-1500 even where STL is permitted; TIA Portal V14 and later issue a warning and the code is no longer portable to SCL. -
PROFIdrive PKW/PZD telegrams (the
Data_receiveandData_sendstructs withPCV_PCA,PCV_IND,Data) require a fixed slot order on the slave side and a consistent slot mapping in HWCN. If the FB is rewritten in SCL without preserving the byte layout (PWE on bytes 8-11 of the PKW region), the drive will reply withPKE = 0x7000(parameter not found) even though the user code looks correct.
Local Variable Addressing Model: S7-300/400 vs S7-1200/1500
The original STL relies on the S7-300/400 local data stack layout. The compiler reserved 32 bytes of L stack per priority class and mapped each declared local (temp) variable to a fixed byte offset inside that frame. The user then addressed bits by literal offset:
| Statement | Meaning on S7-300/400 | Meaning on S7-1200/1500 |
|---|---|---|
A L 1.0 |
AND local data bit at byte 1, bit 0 | Not supported. Compiler error. |
= L 3.1 |
Assign to local data bit at byte 3, bit 1 | Not supported. |
L PIW [AR1, P#10.0] |
Load process input word at pointer (AR1 + 10 bytes) | Warning on S7-1500; rejected if block optimized. |
T PQW [AR1, P#8.0] |
Transfer to process output word at pointer (AR1 + 8 bytes) | Warning on S7-1500; rejected if block optimized. |
On S7-1500 (and therefore on ET 200S IM151-8 PN), the official replacement is to declare an explicit temp variable of type BOOL, INT, WORD, or STRUCT and to address it by symbol. The compiler still places it in a local data area, but the area is opaque to the programmer. See the Siemens SIMATIC S7-1200 Programmable Controller — Data Types reference for the supported scalar and structured types.
Variable Declaration Mapping
The user's variable table is correct in shape; it is the use that breaks. The following mapping is the minimal correct declaration for the SCL/optimized-block equivalent, and is also valid for STL on S7-1500 with V1.5+ if you keep STL:
| Original Name | Type | Section | Replacement Notes |
|---|---|---|---|
IW_INIC |
INT |
Input | Logical base I/O address of the drive (e.g., 256 for IW 256). |
REF_MESTRE |
INT |
Input | Master setpoint (engineering units). |
RR_drv |
BOOL |
Input | Run request from the operator. |
RESET |
BOOL |
Input | Fault reset edge. |
fSim |
BOOL |
Input | Simulation mode enable. |
Hab_Par |
BOOL |
Input | Parameter channel enable. |
fRdWr |
BOOL |
Input | Read/write direction for PKW. |
bRC |
BYTE |
Input | Return code from previous request. |
nPNU |
INT |
Input | Parameter number (PNU). |
bSubIndex |
BYTE |
Input | Parameter sub-index. |
lSetPVA |
REAL |
Input | Setpoint in physical units. |
FALHA_MON |
BOOL |
Input | External fault monitor. |
Drive_OK |
BOOL |
Output | Aggregate "drive healthy" flag. |
Valor_Ref |
INT |
Output | Normalized reference (0-27648). |
lActPVA |
REAL |
Output | Actual process value in engineering units. |
nInState |
INT |
Temp | Status word mirror. |
nOutState |
INT |
Temp | Control word being assembled. |
fDrvRdy |
BOOL |
Temp | Intermediate "drive ready" flag. |
nAdrOff |
WORD |
Temp | Address offset (byte). |
nRetVal |
INT |
Temp | Return value for error reporting. |
Data_receive |
STRUCT |
Temp | {PCV_PCA: INT, PCV_IND: INT, Data: DINT} |
Data_send |
STRUCT |
Temp | {PCV_PCA: INT, PCV_IND: INT, Data: DINT} |
lDiv |
REAL |
Temp | Intermediate divisor. |
{PCV_PCA, PCV_IND, Data} in the original code uses INT for the first two and DINT for the value. That is byte-compatible with PROFIdrive profile V4 only if Data is treated as two consecutive words with no padding. When you retype this in SCL, declare Data as DWORD or ARRAY[0..3] OF BYTE to avoid implicit alignment padding that some TIA Portal versions insert.Step-by-Step Migration Path
Follow the sequence below. Steps 1–3 confirm the cause. Steps 4–7 are the actual fix. Steps 8–9 verify on the running CPU.
- Confirm the target CPU in the TIA Portal device configuration. Open Devices & networks > [your CPU] and read the order number and firmware. If the firmware is on an S7-1200 (6ES7 2xx), STL is unavailable; the FB must be rewritten. If the firmware is on an S7-1500 or an ET 200S IM151-8 PN (6ES7 151-8Axx), STL is available but restricted; rewriting is still recommended for portability.
-
Open the FB and inspect the access pattern. Use Ctrl+F on the literal
L(with trailing space). Every hit that is not a load (L) of a temp variable is a candidate for rewrite. In the original block, hits includeA L 1.0,= L 3.0…= L 3.7,= L 2.2— eight such statements. -
Export the FB as external source. Right-click the FB > Generate source from blocks. This produces a
.srcfile with the original STL body. Keep it as a reference for the bit-layout you must preserve. -
Replace each local-bit access with a named boolean temp. Map the original
L 1.0,L 1.3,L 1.7intobRun,bSim,bMonOK(or similar) and similarly for the eight control-word bitsL 2.2,L 3.0…L 3.7. The mapping is arbitrary as long as the assemblednOutStateends up with the same bit pattern. -
Replace the pointer arithmetic with a symbolic PEEK/POKE or a HWCN tag. Two clean replacements are available:
a. UsePEEK_WORD/POKE_WORDin SCL against a hardware offset. Example:Valor_Ref := PEEK_WORD(area := 16#81, byteOffset := INT_TO_DWORD(#IW_INIC) * 2 + 10) / 2;is the symbolic equivalent ofL PIW [AR1, P#10.0].
b. DeclareDriveInandDriveOutasHW_IO(HW submodules in the device configuration) and reference them by symbolic name. This is the preferred path on S7-1500 and removes the address arithmetic entirely. - Rewrite the bit logic in SCL or LAD. Use the SCL block below as a starting point. It compiles on S7-1200 firmware V4.0+, S7-1500, and ET 200S IM151-8 PN.
-
Keep the PROFIdrive PKW slot mapping identical to the original. If the original used a standard telegram (e.g., Telegram 1 = PZD-2/2, Telegram 3 = PZD-3/5 PPO type 3 with PKW), do not change the HWCN slot configuration. Only the user code that fills
PCV_PCA,PCV_IND, andDatais rewritten. - Compile the project. Open the project tree, right-click the CPU > Compile > All (rebuild). There must be no warnings on the FB. The common remaining warning is "Temporary variable can be merged", which is harmless.
- Download and verify on the running CPU. See the Verification section below.
SCL Replacement for the Bit-Logic Block
The following SCL block is functionally identical to the original STL body, but compiles on every TIA Portal target. Drop it into a new FB named, for example, FB_Drive_IF.
FUNCTION_BLOCK "FB_Drive_IF"
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
VAR_INPUT
IW_INIC : INT; // base logical I/O address
REF_MESTRE : INT; // master setpoint (raw)
RR_drv : BOOL; // run request
RESET : BOOL; // fault reset (edge)
fSim : BOOL; // simulation
Hab_Par : BOOL; // PKW channel enable
fRdWr : BOOL; // 0 = read, 1 = write
bRC : BYTE; // last return code
nPNU : INT; // parameter number
bSubIndex : BYTE; // sub-index
lSetPVA : REAL; // setpoint in engineering units
FALHA_MON : BOOL; // external monitor
END_VAR
VAR_OUTPUT
Drive_OK : BOOL;
Valor_Ref : INT;
lActPVA : REAL;
END_VAR
VAR_TEMP
nInState : INT;
nOutState : INT;
fDrvRdy : BOOL;
nAdrOff : WORD;
nRetVal : INT;
Data_receive : STRUCT
PCV_PCA : INT;
PCV_IND : INT;
Data : DINT;
END_STRUCT;
Data_send : STRUCT
PCV_PCA : INT;
PCV_IND : INT;
Data : DINT;
END_STRUCT;
lDiv : REAL;
iRawIn : INT;
iRawOut : INT;
END_VAR
BEGIN
// --- Bit logic: replaces A L 1.0 / AN L 1.3 / AN L 1.7 ---
fDrvRdy := RR_drv AND NOT fSim AND NOT FALHA_MON;
Drive_OK := fDrvRdy OR fSim;
// --- Scaling: replaces PIW [AR1,P#10.0] / ITD / *D / /D ---
// 27648 = S7 nominal analog range. 16384 = scaling denominator.
iRawIn := PEEK_WORD(area := 16#81,
byteOffset := INT_TO_DWORD(IW_INIC) * 2 + 10);
Valor_Ref := REAL_TO_INT(INT_TO_REAL(iRawIn) * 27648.0 / 16384.0);
// --- Control word assembly: replaces the eight = L 3.x lines ---
nOutState := 0;
IF RR_drv THEN nOutState := nOutState OR 16#0001; END_IF; // bit 0
IF RESET THEN nOutState := nOutState OR 16#0080; END_IF; // bit 7
// bit 1..6 remain SET (no OFF1/OFF2/OFF3 commanded)
nOutState := nOutState OR 16#007E;
// bit 2 reserved (manufacturer-specific ramp enable)
// --- Write control word: replaces T PQW [AR1,P#8.0] ---
POKE_WORD(area := 16#82,
byteOffset := INT_TO_DWORD(IW_INIC) * 2 + 8,
value := INT_TO_WORD(nOutState));
// --- PKW/PZD assembly placeholder ---
Data_send.PCV_PCA := SHL(WORD_TO_INT(INT_TO_WORD(nPNU)), 8)
OR (fRdWr ? 16#01 : 16#00)
OR WORD_TO_INT(INT_TO_WORD(bSubIndex));
Data_send.PCV_IND := 0;
Data_send.Data := REAL_TO_DINT(lSetPVA);
END_FUNCTION_BLOCK
16#81 = process inputs (PI), 16#82 = process outputs (PQ). The byte offset IW_INIC * 2 + 10 reproduces the original SLW 3 / LAR1 / PIW [AR1, P#10.0] pattern, where the SLW 3 shifted the word address to byte address. If your drive uses 16-bit process words, use PEEK_WORD; for 32-bit PZDs use PEEK_DWORD.Indirect Addressing Conversion (AR1 → Symbolic)
The original STL built a byte pointer from #IW_INIC and used AR1 as the base register. The full sequence is:
L #IW_INIC // load logical input address (e.g., 256)
SLW 3 // shift left 3 = multiply by 8 = byte address
LAR1 // load AR1 = P#2048.0
L PIW [AR1, P#10.0] // read PIW at byte offset 2048 + 10
T PQW [AR1, P#8.0] // write PQW at byte offset 2048 + 8
This is the S7-300/400 idiom for indirect addressing with a constant offset into the I/O area. The replacement matrix is:
| Original STL | SCL on S7-1200/1500 (optimized) | SCL using PEEK/POKE |
|---|---|---|
SLW 3 / LAR1 |
(none needed — use symbolic HW tag) | nAdrOff := INT_TO_WORD(#IW_INIC) * 8; |
L PIW [AR1, P#10.0] |
iRawIn := "DB_Drive".InputWord; (where InputWord is mapped in HWCN) |
iRawIn := PEEK_WORD(area := 16#81, byteOffset := INT_TO_DWORD(#IW_INIC) * 2 + 10); |
T PQW [AR1, P#8.0] |
POKE_WORD(area := 16#82, byteOffset := INT_TO_DWORD(#IW_INIC) * 2 + 8, value := INT_TO_WORD(nOutState)); |
The symbolic HW-tag path is preferred: in the device configuration, open the drive submodule, expose the PZDs as I/O addresses, and reference them by tag. This removes the arithmetic, the address-bookkeeping temp nAdrOff, and the dependency on the logical address being a multiple of 8.
PROFIdrive PKW/PZD Channel Rebuild
The two structs Data_receive and Data_send are the parameter channel (PKW). The PROFIdrive profile places them at the head of the telegram when the configured PPO type includes PKW (PPO types 1, 3, and 5 on PROFIBUS, or explicit PKW-enabled telegrams on PROFINET). The byte layout is fixed:
| Word Index | Field | Width | Content |
|---|---|---|---|
| 0 |
PCV_PCA (PKE) |
16 bits | Bits 0–10 = PNU, bit 10 = SPM, bit 11 = reserved, bit 12–15 = AK (request/response ID). |
| 1 |
PCV_IND (IND) |
16 bits | Bits 0–7 = sub-index, bits 8–15 = page index. |
| 2 |
Data word high |
16 bits | Parameter value high word (or unused for byte parameters). |
| 3 |
Data word low |
16 bits | Parameter value low word. |
Common AK codes (bits 12–15 of PKE):
| AK (hex) | Meaning (Request) | Meaning (Response) |
|---|---|---|
| 0x0 | — | No response (parameter access rejected) |
| 0x1 | Read parameter value | Read parameter value (single word) |
| 0x2 | Write parameter value (word) | Write parameter value (single word) |
| 0x3 | — | Read parameter value (double word) |
| 0x4 | Write parameter value (double word) | Write parameter value (double word) |
| 0x6 | Read parameter value (array) | Read parameter value (array, words) |
| 0x7 | Write parameter value (array) | Write parameter value (array) |
| 0x8 | — | Read parameter value (array, double words) |
| 0xD | — | Read parameter value (array, blocks) |
| 0xE | — | Read parameter value (multi-parameter) |
The original code's Data_receive is the read-back slot; Data_send is the request slot. The bit packing shown above for Data_send.PCV_PCA follows the standard. Failure to keep this packing intact is the most common cause of the drive replying with PKE = 0x7000 ("parameter not found") even when the PNU is correct.
Verification and Commissioning
After rewriting, perform the following checks in order. Each step has a single observable pass criterion.
- Offline syntax check. Right-click the FB > Compile > Software (rebuild). The "Error" column must be 0.
-
Watch table — control word. Create a watch table that forces
RR_drv := TRUE,fSim := FALSE,FALHA_MON := FALSE. ObservenOutStatein the FB's instance DB. Expected value:16#007F(bits 0..6 set, bit 7 from RESET = 0). The drive should leave Ready to switch on state. -
Watch table — PKW round-trip. Force
nPNU := 3(drive type, common PNU),fRdWr := FALSE,bSubIndex := 0. Trigger one OB1 cycle and readData_receive.PCV_PCA. Expected: AK =0x1(response = read single word), PNU field = 3, SPM bit clear. If the AK field is0x7, the request has been echoed back rather than answered — check the slot mapping. -
Online — drive status word. Read the PZD that mirrors
ZSW1(status word 1, PROFIdrive). Bit 0 (Ready to switch on) and bit 1 (Ready) must both come on within 200 ms of the first valid control word. If not, the issue is in the telegram mapping, not the FB. -
Online — reference scaling. Set
REF_MESTRE := 16384and observeValor_Ref. Expected: approximately27648(the S7 nominal full-scale). IfValor_Refis half the expected value, the SCL arithmetic has been written with16384and32768swapped. -
Fault reset path. Pulse
RESETfor one cycle.nOutStatebit 7 (control-word bit 7, fault acknowledge on most drives) must toggle. If it does not, the bit mask in the SCL block is wrong (verify16#0080).
Troubleshooting Matrix
| Observed Symptom | Likely Cause | Diagnostic Step | Fix |
|---|---|---|---|
| Compiler error: "Address L does not exist" | STL L-bit access used on S7-1200 or S7-1500 | Search for L tokens in the FB source |
Replace with named BOOL temps |
| Compiler error: "STL not supported" | S7-1200 target | Check CPU order number | Switch to SCL/LAD/FBD |
Valor_Ref = 0 |
PEEK area wrong or offset wrong | Inspect byteOffset calculation |
Use IW_INIC * 2 + 10 for 16-bit PZD |
| Drive replies with PKE = 0x7000 | Wrong AK field in Data_send.PCV_PCA
|
Online view of the PZD | Re-pack AK into bits 12–15 of PCV_PCA
|
nOutState stuck at 0 |
Bit-by-bit assignment block was not entered (BB condition) | Step through with single-cycle breakpoints | Confirm RR_drv is TRUE at the assignment |
| Block downloads but drive does not respond at all | Telegram mismatch in HWCN | Compare configured PPO type with drive firmware | Re-add the drive in HWCN and re-bind the PZD/PKW slots |
| Implicit warning on S7-1500: "AR1/AR2 not allowed in optimized block" | Old STL pointer idiom retained | Search for AR1, AR2 in FB source |
Switch to symbolic I/O access or PEEK/POKE |
| Compilation succeeds on S7-1500 STL but execution differs from S7-300 | Compiler re-ordered locals; old L 1.0 now points to a different bit |
Add a unique sentinel write at the start of the FB | Rewrite the body without L-stack literals |
Field-Proven Caveats
- Don't keep STL "just because it works on the old CPU". Once the ET 200S IM151-8 PN/DP is in scope, you are on an S7-1500-class firmware. The next firmware update will likely tighten the STL restrictions further.
-
Check the block's "Optimized block access" attribute. If the FB was created with optimized access = TRUE (default on TIA Portal V14+), you cannot use absolute
LorAR1access at all. Switch the attribute to FALSE only as a last resort for legacy STL, and document the trade-off. - The scale factor pair (27648 / 16384) corresponds to the S7 analog-input nominal range (27648) divided by the half-range of a normalized reference (16384 = 2^14). For unipolar 4–20 mA or 0–10 V signals, this is correct. For bipolar (±10 V) signals, use 27648 with a signed offset; for full 16-bit raw range, use 65535 / 32768 instead.
- PKW vs PZD separation. Some drives (notably older SIMOREG and SIMOVERT) swap the PZD-1 and PZD-2 direction relative to the modern SINAMICS mapping. Verify with the drive's parameter manual; the FB code is otherwise identical.
- CPU ET 200S IM151-8 communication errors are independent of this FB. If the FB compiles, downloads, and the FB's outputs toggle, but the drive does not respond, the issue is on the bus side (PROFINET name, slot mismatch, watchdog, or PKW length = 4 words but the drive is configured for 8). Open a separate diagnostic for the network path; the local-variable rewrite will not fix it.
FAQ
Does the S7-1200 support STL?
No. The S7-1200 supports LAD and FBD only. SCL is supported from firmware V4.0 onward. To migrate STL code to an S7-1200 target, rewrite the block in SCL or LAD/FBD. Reference the SIMATIC S7-1200 manual collection on docs.tia.siemens.cloud for supported languages and data types.
Can the ET 200S IM151-8 PN/DP run STL blocks compiled in TIA Portal?
Yes, but the STL behaves like S7-1500 STL: AR1/AR2 pointer arithmetic, area-crossing pointer math, and absolute L-stack bit access are restricted. The recommended migration is to SCL with optimized block access enabled.
How do I convert A L 1.0 / = L 3.2 style bit access to TIA Portal?
Declare a named BOOL temp for each bit-slice you need, replace A L 1.0 with IF bBit_1_0 THEN, and replace = L 3.2 with a direct boolean assignment. For control-word assembly, OR-mask the bits into an INT temp such as nOutState := nOutState OR 16#0004;.
What replaces PIW [AR1, P#10.0] in TIA Portal SCL?
Use PEEK_WORD(area := 16#81, byteOffset := <offset>) for direct access, or expose the PZD as a symbolic HW tag in the device configuration. For 32-bit PZDs, use PEEK_DWORD. Avoid the AR1 pattern on S7-1500 because it is rejected in optimized blocks.
Why does nOutState stay at zero even after I rewrite the bit assignments?
Most often the assignment block is gated by an earlier BB or the implicit ENO is being cleared by a divide-by-zero in the Valor_Ref line. Check the instance DB online with Monitor & force: if lDiv is zero, the SCL block exits early. Add a RETURN guard after the scaling and re-test.