Siemens S7 STL Programming: Indexed Addressing and Profibus DP

David Krause16 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

Overview of S7 Programming Languages

Siemens S7-300 and S7-400 controllers (and the integrated S7-300/400 inside the S7-1500 via legacy support packages) expose four or five IEC 61131-3 languages to the engineer: LAD (Ladder Diagram), FBD (Function Block Diagram), STL (Statement List, sometimes called AWL), SCL (Structured Control Language), and on S7-1500/1200 the addition of GRAPH (sequential function chart) and a refreshed CFC. STL is the only one of these that gives the programmer direct visibility of the accumulator, status word, address register, and RLO flow at the bit level. That visibility is exactly why veteran control engineers reach for STL when the application has dense math, indexed access into a process image, or peer-to-peer data handshakes that have to fit inside a single Profibus DP cycle.

For motion and high-speed process projects, the official Siemens position is that STL remains valid but SCL is the strategic replacement. The trade-off is throughput versus developer ramp-up. STL executes with the lowest interpreter overhead in the S7-300/400 CPU firmware, but it does not scale to large code bases the way SCL does. The recommendation in Siemens entry ID 109751795 is to keep STL inside optimised function blocks (FB) that are called from an SCL or LAD wrapper, so you gain STL's runtime behaviour without losing the readability of the rest of the project.

Language CPU Cycles / Statement Indexed Memory Access Math Density Best Use
LAD / FBD ~3-7 μs typical Indirect only via block interface Low (limited instruction set) Boolean logic, simple sequencing
STL ~0.1-0.3 μs typical Native via AR1/AR2 and DBX High Indexed loops, Profibus handshake, motion glue code
SCL Compiler optimised, ~1-4 μs typical Array index syntax Very high Recipes, math, complex state machines
GRAPH ~5-15 μs per step Not applicable Low Sequential processes with discrete steps
Cycle times above are representative figures from S7-319 PN/DP firmware V3.x with bit-logic intensive code. Real times vary with the CPU type, the OB1 priority class, and whether communication interrupts (OB40-OB47) are configured. Always measure with SFC 87 "C_DIAG" or via the diagnostic buffer, do not assume vendor brochure values.

STL Statement List Fundamentals

An STL statement operates on one of three classes of operand: accumulators (ACCU1, ACCU2), status bits (RLO, STA, OR, OS, OV, CC0, CC1, BR), and address registers (AR1, AR2). The most common mental model for engineers moving from LAD is to read STL top-to-bottom as if the engineer were narrating the operations verbally: "load, compare, jump, store." STL is not a register machine in the C-language sense, but the accumulator metaphor is close enough that engineers familiar with assembly on x86 or ARM pick it up quickly.

Minimum instruction set needed for productive STL work:

Operation STL Mnemonic Effect
Load L Push operand into ACCU1, prior ACCU1 moves to ACCU2
Transfer T Copy ACCU1 to operand memory
Compare >I, <I, ==I, <>I Sets CC0/CC1, RLO
Bit logic A, O, X, AN, ON, XN AND, OR, XOR with RLO
Set/Reset S, R Bit-level set/reset, RLO dependent
Jump JU, JC, JCN, JL, LOOP Unconditional, conditional, list, decrement-and-jump
Block call CALL, UC, CC Conditional/uncalled FB/FC/SFB/SFC
DB register OPN, CDB, DBNO Open data block, exchange DI/DI2
Address register +AR1, +AR2, LAR1, LAR2, TAR1, TAR2 Pointer arithmetic

Minimum STL program skeleton for an FB that increments a counter every cycle:

FUNCTION_BLOCK FB100
VAR
  Cnt : INT;       // instance memory
END_VAR
BEGIN
  L     #Cnt;          // load current count
  L     1;             // push constant
  +I;                  // ACCU2 + ACCU1 -> ACCU1
  T     #Cnt;          // store back
  L     #Cnt;
  L     1000;          // limit
  >I;
  JC    RESET;         // jump if count > limit
  BEU;                 // block end unconditional
RESET: L     0;
  T     #Cnt;
END_FUNCTION_BLOCK

This skeleton is the basic loop most textbooks use; production code should also handle overflow (OV bit) and use the LIMIT STL instruction for clamp-to-range in one cycle.

Indexed Memory Access with AR1 / AR2

The single biggest productivity jump an engineer gets from STL is indexed access into the process image, the bit memory, or an instance DB. LAD supports this only through block interface variables; STL exposes the AR1 and AR2 registers, plus the DBX/DBW/DBD indirect area-cross-pointer syntax.

There are three flavours of pointer in S7-300/400 STL:

Pointer Width Syntax Range Use Case
32-bit area-cross (internal) P#DB100.DBX0.0 BYTE 20 16 MB address space Cross-DB indexed reads/writes
32-bit area-internal P#10.0 65535 bytes within one area Loop inside one DB or PI
48-bit DB pointer (legacy S7-200 style) DBW 0 as word Up to DB 32767 Only when importing S7-200 migration

A typical indexed walk through 100 WORD entries in DB200 to find the first non-zero value:

OPN   DB200;                  // open target DB
LAR1  P#0.0;                   // pointer to first word
L     100;                     // loop counter
NEXT: T     #LoopCnt;
      L     DBW [AR1,P#0.0];    // area-internal indirect read
      <>I;
      JC    FOUND;
      +AR1  P#2.0;              // step by 2 bytes (one WORD)
      L     #LoopCnt;
      LOOP  NEXT;
      L     0;
      T     #ResultIdx;
      BEU;
FOUND: L     100;
      L     #LoopCnt;
      -I;
      T     #ResultIdx;         // 0..99 index
END_FUNCTION_BLOCK

The DBW [AR1,P#0.0] syntax is the heart of area-internal indirect access. The AR1 register holds the base pointer, the constant P#0.0 is the offset (here zero). Adding +AR1 P#2.0 advances the pointer by one WORD per iteration. This syntax is documented in the Siemens STL manual (entry ID 109751806) and is the form most training courses teach.

For area-cross (i.e. switching which DB you read from inside the loop), use the 32-bit area-cross pointer:

OPN   DI 200;                 // open instance DB
LAR1  P#DBX 0.0 BYTE 10;       // byte 10 of DB200
L     DIW [AR1,P#0.0];         // indirect word read via area-cross pointer

Mixing area-internal with area-cross without reloading AR1 is a common bug source. If AR1 was last set to an area-internal pointer (P#10.0) and the code then executes L DBB [AR1,P#0.0], the S7 CPU still interprets the pointer as a 32-bit area-cross value and reads from DB0 / bit memory 0, which is rarely what the engineer intended. Always normalise AR1 by LAR1 P#DBX 0.0 BYTE 0 at function start if the FB is shared between call sites.

Profibus DP Master-Slave Handshake

Profibus DP (Decentralised Periphery) on S7-300/400 is centred on the DP master interface (CP 342-5, IM 308-C, or the integrated DP port on CPUs such as 315-2DP and 319-3PN/DP). The PROFIBUS Installation Guideline and IEC 61158-6 define the wire protocol; the S7 side is documented in the SIMATIC S7-300 Profibus DP manual (entry ID 109751784).

DP data exchange is a deterministic cyclic polling of slave I/O areas. Each slave is configured with GSD file parameters that declare the input and output lengths. The master writes outputs and reads inputs in a single telegram; with cyclic master-master mode (DX) or with acyclic RD_REC/WR_REC services the application gets parameter channels.

For data longer than one Profibus DP telegram (32 bytes input / 32 bytes output on most DP-V0 slaves), the engineer must implement a multi-telegram handshake. The standard pattern is a token-passing mailbox: the master increments a sequence number, the slave echoes it, and a new telegram is requested only after a matching echo arrives.

Step Master Output Slave Output Status
Idle SEQ = 0, REQ = 0 SEQ = 0, ACK = 0 No pending transfer
Request SEQ = 1, REQ = 1, payload[N] SEQ = 0, ACK = 0 New data sent
Acknowledge SEQ = 1, REQ = 0 SEQ = 1, ACK = 1 Slave received
Release SEQ = 0, REQ = 0 SEQ = 0, ACK = 0 Cycle complete, ready for next

STL implementation of the master side of the handshake inside a cyclic OB1:

// Inputs are mapped to PIW and outputs to PQW by HW config
// Assuming output area starts at PQW 256 with 8 words,
// and input area at PIW 256 with 8 words.
NETWORK 1  // Increment SEQ on new data
  A     M    100.0;            // application "new data" flag
  AN    M    100.1;            // handshake busy
  JCN   NO_REQ;
  L     MW   110;              // current SEQ
  L     1;
  +I;
  T     MW   110;
  S     M    100.1;            // set busy
NO_REQ: NOP 0;

NETWORK 2  // Build output telegram
  L     MW   110;              // SEQ
  T     PQW  256;              // word 0 of output area
  L     DB10.DBW 0;            // payload word 0
  T     PQW  258;
  L     DB10.DBW 2;
  T     PQW  260;
  // ... up to PQW 270 (8 words total)

NETWORK 3  // Read and verify input telegram
  L     PIW  256;              // SEQ echo from slave
  L     MW   110;
  <>I;
  JC    TIMEOUT;               // SEQ mismatch = error
  L     PIW  258;              // payload word 0
  T     DB11.DBW 0;
  L     PIW  260;
  T     DB11.DBW 2;
  // ...

NETWORK 4  // Clear busy on successful echo
  AN    M    100.2;            // application "consumed" flag
  R     M    100.1;
  BEU;

TIMEOUT: S    M    100.3;       // handshake error latched
  R     M    100.1;
END

The handshake must be visible from both sides. The slave's firmware (if the slave is a Siemens ET 200S, ET 200M, or third-party DP-V1 slave) returns the SEQ echo automatically if the I/O area is correctly mapped; the engineer's job is to verify the echo every cycle and to time out if no echo arrives within N DP cycles.

Use OB 82 (diagnostic interrupt) and OB 86 (rack failure) to capture slave loss. Diagnose the slave with SFC 13 "DPRTM" or SFB 52 "RDREC" for DP-V1 extended diagnostics. Wire SFC 13 to the DP slot of the slave; the returned diagnostic buffer tells you whether the error is wire break, configuration mismatch, or slave firmware fault.

Motion Controller Integration with S7

Siemens motion controllers - FM 353, FM 354, FM 357-2, and the newer SIMOTION D4x5 series - all support a Profibus DP-V2 slave interface. For DP-V2 the slave is time-synchronous (isochronous mode), which gives the application a deterministic position update rate down to 250 μs, depending on the configured equidistant DP cycle. Configuration steps in SIMOTION SCOUT engineering manual:

  1. Configure the S7-300/400 as DP master in HW Config, add the SIMOTION slave from the GSD file (Siemens provides SIM443.--- and similar GSDs).
  2. Select isochronous mode on the slave slot; the master OB is then triggered by OB 61-OB 64 (synchronisation cycle).
  3. Map at least 16 bytes of inputs and 16 bytes of outputs. The SIMOTION standard telegram 105 includes 8 words of position/velocity and 8 words of setpoints.
  4. In the S7 program, read/write these I/O areas inside OB 61 to align to the isochronous tick.

Sample STL in OB 61 to read position and write target velocity:

NETWORK 1  // Read position from SIMOTION telegram 105
  L     PIW  320;              // word 0, position low
  T     DB200.DBW 0;
  L     PIW  322;              // word 1, position high
  T     DB200.DBW 2;
  L     PID  324;              // double word 2, actual velocity
  T     DB200.DBD 4;

NETWORK 2  // Write target velocity
  L     DB200.DBD 100;         // setpoint from HMI
  T     PQD  320;              // first 4 output bytes

The cross-vendor alternative is to use the SIMOTION "technology object" interface and drive it via Profidrive profile (PROFIdrive V3.0 / V4.0). PROFIdrive is standard in IEC 61800-7 and is documented in the PROFIdrive profile specification. For Profidrive, the engineer writes a control word (STW1) and a speed setpoint (NSOLL) to the slave; the slave returns a status word (ZSW1) and actual speed (NIST).

State machine for PROFIdrive Application Class 1 (velocity mode):

State STW1 bit pattern Action
S1 Ready to switch on 0000 0110 Power electronics ready, no torque
S2 Switched on 0000 0111 Output stage enabled, no setpoint
S3 Operation enabled 0000 1111 Drive follows setpoint
S4 Quick stop 0000 1011 Ramp down with quick-stop ramp
S5 Fault 0000 1111 (with fault bit) Drive acknowledges fault, ready to reset

Block Architecture: FB, FC, DB, UDT

The pattern recommended for S7 projects above ~2,000 STL lines is the multi-instance FB architecture. A UDT (User-Defined Type) declares a recurring data structure; an FB uses the UDT as an input, output, or static variable; multiple instances of the FB share the same instance DB.

TYPE UDT_MOTION
  STRUCT
    SetPos       : DINT;   // target position in encoder units
    ActPos       : DINT;   // actual position
    SetVel       : REAL;   // target velocity in mm/s
    ActVel       : REAL;
    Enable       : BOOL;   // drive enable
    Fault        : BOOL;
    SeqNum       : INT;    // handshake token
  END_STRUCT;
END_TYPE

The motion FB then uses the UDT as its static VAR:

FUNCTION_BLOCK FB200
VAR
  State : UDT_MOTION;       // instance-local copy
END_VAR

Calling 50 motion axes from OB1 in a machine with 50 axes consumes one DB block per call site (DB200, DB201, ... DB249) only if the engineer uses single-instance FB calls. Multi-instance means a single DB250 holds 50 copies of the UDT, accessed via the "FB200".State.SetPos[Idx] notation in SCL, or via DBX arithmetic in STL.

Watch the instance-DB size limit. S7-300 allows instance DBs up to 16 KB on a CPU 315-2DP, but S7-400 CPU 416 extends to 64 KB. If the multi-instance DB exceeds the limit, STEP 7 will refuse to download. Split the call into two FBs and two instance DBs at the boundary.

Indirect Addressing in Function Blocks

A frequent real-world requirement is to read 16 measurement channels from a third-party Profibus slave into an instance DB. STL implementation with the area-internal pointer:

FUNCTION_BLOCK FB300
VAR_INPUT
  StartIdx : INT;
  EndIdx   : INT;
END_VAR
VAR
  Idx      : INT;
END_VAR
BEGIN
  OPN   DB300;                   // data destination
  L     #StartIdx;
  ITD;                            // INT to DINT for pointer math
  SLD   3;                        // multiply by 8 bits = 1 byte
  +AR1                            // add to existing AR1? use LAR1 instead
  // Correct pattern:
  LAR1  P#0.0;
  L     #StartIdx;
  ITD;
  SLD   3;                        // byte offset
  +AR1;                           // AR1 = base + offset
  L     #EndIdx;
  L     #StartIdx;
  -I;
  LOOP NEXT;
  BEU;
NEXT: T     #Idx;
  L     PIW [AR1,P#0.0];         // read PIW at offset
  T     DBW [AR1,P#0.0];         // write to DB300
  L     #Idx;
  LOOP NEXT;                      // decrement ACCU1, jump if not zero
END_FUNCTION_BLOCK

The SLD 3 instruction is the bit-shift trick to convert an INT index to a byte pointer (each INT shift left by 1 = byte boundary; shift left by 3 = byte boundary × 2 = word boundary × 4 ...). For DOUBLE word access, SLD 5.

Error Handling and Diagnostics

STL gives direct access to the status word bits, which the application should monitor for division by zero (OV), illegal floating-point (OS), or accumulator overflow (UO from ==R). Wrap critical math in:

NETWORK 1  // divide with overflow capture
  L     DB10.DBD 0;             // numerator
  L     DB10.DBD 4;             // denominator
  SRD   1;                      // shift to position divisor
  /R;                            // floating divide
  T     DB10.DBD 8;             // result
  UN    OV;                     // no overflow?
  SPB   OK;
  L     0.0;
  T     DB10.DBD 8;
  S     DB10.DBX 12.0;          // set "div error" flag
  SPA   CONTINUE;
OK:  R     DB10.DBX 12.0;
CONTINUE: NOP 0;

The diagnostic buffer of the S7 CPU (read with SFC 51 "RDSYSST" with SSL_ID W#16#0131 / W#16#0F31) lists the last 50 events with timestamp and event ID. Engineers integrating Profibus should poll this buffer in OB 82/OB 86 so that a slave going offline at 03:42:15 produces a time-stamped alarm in WinCC instead of an unexplained loss of view.

Best Practices Summary

  1. Keep STL inside FBs that have a single responsibility. The compiler optimises better and the reviewer can audit the math.
  2. Always reset AR1 at function start with LAR1 P#DBX 0.0 BYTE 0 if the FB is shared between call sites with different data blocks.
  3. Use multi-instance DBs for repetitive structures (axes, channels, drives). One large DB is easier to back up than 50 small ones.
  4. Use the area-internal 32-bit pointer (P#x.y) for loops inside a single area; use the area-cross pointer (P#DBx.DBX y.z BYTE n) only when switching DBs mid-function.
  5. Implement a SEQ-ACK handshake for any Profibus DP payload longer than 32 bytes; never trust that two consecutive cycles of identical data mean the slave has read the new value.
  6. Monitor the status word (OV, OS, UO) around every floating-point instruction; one missed check cascades into a corrupt recipe.
  7. Time out Profibus handshakes after 5-10 DP cycles (configurable). A slave that drops off the bus mid-transfer otherwise leaves the master waiting forever.
  8. Use isochronous mode (OB 61-OB 64) for motion control. Cyclic OB1 jitter is too high for precision positioning; the isochronous interrupt is locked to the DP cycle start.
  9. Read the diagnostic buffer in OB 82/OB 86 with SFC 51; surface the events in WinCC so the operator sees the cause, not just the symptom.
  10. Document the STL blocks with the TITLE = and AUTHOR = block attributes; the comment is uploaded to the CPU and visible in the online view.

Verification Procedure

After loading STL into the S7 CPU, perform the following checks before sign-off:

  1. Open the program in STEP 7 and verify the block consistency. Options > Block Consistency should report zero errors.
  2. Online: go to the FB in question and watch the STL with monitor on. Check that the accumulator and status word bits transition as expected when you toggle inputs.
  3. Single-scan: use CRTL+F8 or PLC > Monitor/Modify > Single Scan to step one cycle at a time. Verify that AR1 after the loop is the expected value, not an orphan pointer.
  4. Force a Profibus slave to power-cycle. Watch the diagnostic buffer for OB 86 entry with the correct slot number and rack number.
  5. Trigger an OB 1 cycle overrun (add a long loop). Verify the CPU enters STOP with diagnostic event "OB1 cycle time exceeded" and the correct event ID.
  6. Back up the S7 project with Archive and verify the archive opens cleanly on a second engineering station.

FAQ

What is the difference between AR1 and AR2 in S7 STL?

AR1 is the primary address register, used for all indirect addressing via DBX [AR1,P#0.0] and similar syntax. AR2 is the secondary register, used by the system for some block calls and available to the application for parallel addressing operations. Most engineers use AR1 only and treat AR2 as reserved for the compiler.

Can STL call an SCL block or vice versa?

Yes. STL can call any FB, FC, SFB, or SFC regardless of the language it is written in. The block interface (VAR_INPUT, VAR_OUTPUT, VAR_IN_OUT, VAR_TEMP) is language-neutral. The only restriction is that multi-instance variables declared inside an SCL FB cannot be used as the target of indirect STL writes without a static interface variable.

How do I migrate an S7-200 STL project to S7-300/400 or S7-1500?

Use the S7-200 migration tool in STEP 7 V5.x to convert the 200-STL to S7-300/400 STL, then re-test every block. The pointer format changes from 48-bit S7-200 to 32-bit area-cross S7-300, and the absolute address ranges expand. Direct migration to S7-1500 is not supported; use a re-implementation in SCL for portability.

Why does my Profibus DP handshake stall after the first telegram?

Most often the slave's input/output lengths are misconfigured in HW Config, so the slave's echoed SEQ is read from a different word offset than the master wrote. Verify the slot configuration with SFC 13 "DPRTM" or by looking at the DP slave diagnostic buffer. The second most common cause is a CRC or parity error on the segment; check the repeater LEDs and the segment terminator.

Is STL still supported on S7-1500?

STL is not a first-class language in TIA Portal for S7-1500; LAD, FBD, SCL, and GRAPH are the four supported. STL is supported only on S7-300/400 within TIA Portal via legacy blocks. For new development on S7-1500, use SCL for procedural code and GRAPH for sequential processes. For motion control on SIMOTION, SCL with the technology object library is the strategic path.

Back to blog