S7-300 Indirect Addressing in TIA Portal V16: Summing 30 DBs

David Krause15 min read
S7-300SiemensTutorial / 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

S7-300 Indirect Addressing in TIA Portal V16: Summing 30 DBs

Adding 30 data blocks (DB1 through DB30), each containing three double-word (DINT) values at byte offsets 100, 200, and 300, results in 90 individual arithmetic operations. The naive approach of inserting an ADD block per operand creates an unmaintainable program, and the suggested STL alternative raises legitimate questions about the S7-300 memory model, address register use, and TIA Portal V16 SCL syntax. This reference consolidates the working SCL pattern using PEEK_DWORD, the STL register-indirect equivalent, the boundary conditions for TIA Portal V16 on S7-300 firmware V3.x, and the diagnostic steps to verify the sum on PLCSIM or a real CPU.

Problem Definition

The task is to compute the scalar sum:

sum = Σ (i=1..30) [ DBD_i_100 + DBD_i_200 + DBD_i_300 ]

Each DBD is a 32-bit DINT, so the operand total is 30 × 3 = 90 reads and 89 additions. With an ADD block per operand, the network becomes 90 blocks long and any DB-number change requires manual rewiring. The right solution exploits the fact that the DB number and the byte offset are the only varying inputs; both are integers that can drive a single loop.

Scope note. This article targets S7-300 CPUs (CPU 312, 314, 315-2 DP/PN, 317, 319) configured in TIA Portal V16 (engineering build V16, SCL compiler for S7-300/400). S7-1500 syntax (AT%DB1 area, slice access, PEEK with implicit DB) differs and is not addressed here.

Prerequisites

  1. TIA Portal V16 (Build 16.0 or later) installed with the S7-300 add-on selected in the TIA setup.
  2. S7-300 CPU with firmware V3.2 or later (CPU 31x PN/DP). SCL for S7-300/400 requires the optional SCL package to be present in TIA Portal; it is no longer a separate license in V16, but the SCL compiler must be enabled in the project properties (right-click the S7-300 station → Properties → Protection → "Permit access with PUT/GET" is not relevant; check the SCL editor is installed under "Options → Support Packages").
  3. S7 program containing the 30 DBs. Each DB must declare the values at byte offsets 100, 200, and 300. Using non-optimized access is mandatory on S7-300 because PEEK_DWORD uses absolute byte offsets.
  4. S7-PLCSIM V16 or an online connection to the target CPU for verification.

Indirect Addressing on the S7-300

S7-300 supports three flavors of indirect addressing, all available in TIA Portal V16:

  • Memory-indirect addressing – the operand address is a word or double-word in M, L, or DB that the CPU adds to the address register at runtime.
  • Register-indirect (area-internal) – AR1 or AR2 holds the offset inside the currently opened area (DB, DI, M, …).
  • Register-indirect (area-crossing) – the pointer loaded into AR1/AR2 includes the area identifier (16#85 for DB) and the DB number, allowing OPN DB and field reads to use one pointer.

STL instructions that consume these pointers include L DBD [AR1,P#0.0], OPN DB [AR2,P#0.0], L P#area, and LAR1 P#pointer. SCL exposes a much smaller surface but is sufficient for this problem: the standard compiler functions PEEK_BOOL, PEEK_BYTE, PEEK_WORD, and PEEK_DWORD read from a (area, dbNumber, byteOffset) triple, and POKE_BYTE / POKE_WORD / POKE_DWORD / POKE_BOOL write the same way.

Why STL Is Not Required

The S7-300 SCL compiler emits STL, so the underlying instruction mix is identical. The maintenance benefit of SCL is that the index loop compiles to register-indirect machine code, removing the need to maintain a jump chain. A correctly written SCL program is also easier to step through in the TIA Portal debugger, because the variable watch reflects the loop iterator and the intermediate sum without manual register monitoring.

Start OB1 iSum := 0 FOR iDB := 1 TO 30 BY 1 PEEK_DWORD(off=100) iSum += dVal PEEK_DWORD(off=200) iSum += dVal PEEK_DWORD(off=300) iSum += dVal End FOR / Output iSum

Method 1 – SCL with PEEK_DWORD

The cleanest implementation uses a single FOR loop. PEEK_DWORD returns a 32-bit value; the result is cast to DINT for arithmetic, then summed into the accumulator. The complete function block is:

// FB_Sum30DBs – sum of 90 DINTs across 30 DBs
FUNCTION_BLOCK FB_Sum30DBs
VAR
    iDB    : INT;       // 1..30
    iSum   : DINT;      // accumulator
    dVal   : DINT;      // scratch
END_VAR

BEGIN
    iSum := 0;

    FOR iDB := 1 TO 30 BY 1 DO
        dVal := DINT_TO_DINT( PEEK_DWORD(
                    area       := BYTE#16#85,   // DB area
                    dbNumber   := WORD#iDB,     // 1..30
                    byteOffset := DINT#100 ));  // byte offset
        iSum := iSum + dVal;

        dVal := PEEK_DWORD(
                    area       := BYTE#16#85,
                    dbNumber   := WORD#iDB,
                    byteOffset := DINT#200 );
        iSum := iSum + dVal;

        dVal := PEEK_DWORD(
                    area       := BYTE#16#85,
                    dbNumber   := WORD#iDB,
                    byteOffset := DINT#300 );
        iSum := iSum + dVal;
    END_FOR;
END_FUNCTION_BLOCK

Area Constant Reference

Constant (BYTE) Area Source
16#81 P / PE Process inputs (periphery)
16#82 PA / PQ Process outputs (periphery)
16#83 I / IE Inputs (PII)
16#84 M Bit memory
16#85 DB / DI Data block (current or addressed by dbNumber)

When area := 16#85, the dbNumber parameter selects the DB. The SCL compiler emits an internal OPN DB and a register-indirect load, then restores the previous OPN state on return. The call is therefore re-entrant; nested calls in different priority classes are safe as long as the CPU is S7-300 V3.x and the DB number is in the legal range 1..32 767.

Method 2 – SCL with POINTER and FOR Loop

Where the offset itself is dynamic (for example, when the byte offsets are configured in an HMI table or read from a recipe), a POINTER-based variant removes the hardcoded 100/200/300 values:

VAR CONSTANT
    OFFSETS : ARRAY[1..3] OF DINT := [100, 200, 300];
END_VAR

VAR
    iDB   : INT;
    iOff  : INT;
    pData : POINTER;
    dVal  : DINT;
    iSum  : DINT;
END_VAR

BEGIN
    iSum := 0;

    FOR iDB := 1 TO 30 BY 1 DO
        FOR iOff := 1 TO 3 BY 1 DO
            pData := P#DB[INT_TO_WORD(iDB)].DBX[OFFSETS[iOff]].0;
            dVal  := DWORD_TO_DINT( PEEK_DWORD(
                         area       := BYTE#16#85,
                         dbNumber   := WORD#iDB,
                         byteOffset := OFFSETS[iOff] ));
            iSum  := iSum + dVal;
        END_FOR;
    END_FOR;
END
S7-300 pointer caveat. S7-300/400 SCL supports the POINTER data type, but dereferencing with the ^ operator is restricted on this platform. PEEK_* / POKE_* with the explicit (area, dbNumber, offset) signature is the safe path. Do not rely on pData^ to compile on every S7-300 firmware revision.

Method 3 – STL Register-Indirect

STL is still legal in TIA Portal V16 for S7-300/400. The cleanest mapping for the same task uses one AR register and a memory word holding the DB number:

// FC_Sum30DBs_STL – S7-300 STL implementation
// Output: SUM (DINT) in local word 0
// Temp:   iDB (INT) in local word 2

       L     0
       T     #iSum
       L     1
       T     #iDB

NEXT:  NOP   0
       OPN   DB [#iDB]            // Open DB by number from MW

       L     DBD 100
       L     DBD 200
       +D
       L     DBD 300
       +D
       L     #iSum
       +D
       T     #iSum

       L     #iDB
       +     1
       T     #iDB
       L     30
       >=I
       JC    END
       JU    NEXT

END:   NOP   0

To use an area-crossing pointer instead of a memory word – the historical Siemens-recommended pattern – encode the area and DB number into the pointer and let OPN DB [AR1,P#0.0] do the rest:

       LAR1  P##DB_TPL             // load template pointer
       L     1
       T     #iDB

NEXT:  NOP   0
       OPN   DB [AR1,P#0.0]
       L     DBD 100
       L     DBD 200
       +D
       L     DBD 300
       +D
       L     #iSum
       +D
       T     #iSum

       +AR1  P#1.0                 // pointer offset = 1 byte per DB number
       L     #iDB
       +     1
       T     #iDB
       L     30
       >=I
       JC    END
       JU    NEXT

END:   NOP   0

DB_TPL: P#DB1.DBX 0.0             // template; AR1 indexes over the DB byte

The area-crossing pointer approach is what Siemens documented in the original S7-300/400 STL handbooks; it eliminates the OPN DB cycle by encoding the area and DB number into the pointer. The maintenance cost is the template pointer and the loop index discipline.

Selection Guide

Criterion SCL + PEEK_DWORD STL register-indirect
Code length ≈25 lines ≈30 lines
Step-through debug High (variable watch) Low (STL monitor only)
Compiler output PEEK_DWORD expands to L DBD [AR1,P#0.0] Direct emit, one less call
Re-entrancy Yes (compiler manages OPN state) No (OPN DB is global)
Readability for non-STL users High Low
CPU cycle cost on CPU 315-2 PN/DP ≈12 µs per call ≈9 µs per iteration

For 30 iterations, the cycle-time difference is in the order of 100 µs – negligible against a typical OB1 scan of 5–20 ms. Pick SCL unless the CPU is at the watchdog limit and the surplus is needed elsewhere.

Data Block Requirements

For PEEK_DWORD to read a DINT at a precise byte offset, the DB must use non-optimized access. The path is:

  1. Right-click the DB in the project tree → Properties → Attributes.
  2. Confirm that "Optimized block access" is unchecked. (S7-300/400 only ever supported the non-optimized model; the checkbox exists for compatibility checking when the DB is shared with an S7-1500 station.)
  3. Compile and download.

If the DBs were created from a UDT or imported from an S7-1500 export, they may have the optimized flag set; TIA Portal V16 will refuse to compile PEEK calls against optimized DBs on S7-300 and the SCL editor will flag the call site. Convert each DB to non-optimized in bulk by selecting them all in the project tree, opening Properties, and clearing the checkbox – the structure, initial values, and download slots are preserved.

S7-300 hard fact. Optimized data blocks were introduced with S7-1500. S7-300 only supports the absolute-addressing (non-optimized) DB model. PEEK_* therefore works on every S7-300 DB, but the related tool tip about "optimized" in newer TIA Portal dialogs does not apply to this CPU family.

Overflow and Numeric Range

DINT range is −2 147 483 648 .. +2 147 483 647. Ninety values near the extremes push the sum to roughly ±1.9 × 10¹¹, which fits. If the per-DB values come from a 16-bit ADC (range −32 768 .. +32 767) the sum is bounded at 30 × 3 × 32 768 = 2 949 120, well within DINT. If the values come from a 32-bit counter or process accumulator, the bound is tight and the LREAL variant below is the safer choice:

VAR
    rSum : LREAL;     // 64-bit float accumulator
END_VAR

BEGIN
    rSum := 0.0;
    FOR iDB := 1 TO 30 BY 1 DO
        rSum := rSum + DINT_TO_LREAL(
                       PEEK_DWORD(BYTE#16#85, WORD#iDB, DINT#100) ) +
                     DINT_TO_LREAL(
                       PEEK_DWORD(BYTE#16#85, WORD#iDB, DINT#200) ) +
                     DINT_TO_LREAL(
                       PEEK_DWORD(BYTE#16#85, WORD#iDB, DINT#300) );
    END_FOR;
END

LREAL is supported on every S7-300 CPU V3.x but with no hardware FPU – the runtime cost is roughly 6–10 µs per floating-point operation, still acceptable for a 30-iteration loop. If the integer path is preserved, use DWORD_TO_DINT only when the source can be negative; the bit pattern is preserved by PEEK_DWORD and the cast is a no-op at the bit level.

Edge Cases and Pitfalls

DB Number Outside the Project

PEEK_DWORD does not validate that the DB exists in the S7-300 online program. On a real CPU, reading a non-existent DB raises OB121 (programming error) and the CPU goes to STOP if OB121 is not loaded. Defensive coding wraps the call with a try-catch equivalent – S7-300/400 SCL does not have try/catch, so the workaround is to verify the DB number against a constant array of valid DBs before the call, or to load a stub OB121 that increments a non-retentive error counter and returns.

OPN DB Side Effects

PEEK_DWORD opens and closes the DB internally. The OPN state seen by the caller is preserved, but if the caller is running in OB1 and an interrupt OB (OB35, OB82) executes the same FC in parallel, the two will interleave OPN states. Mitigations: keep PEEK in OB1 only, or duplicate the FC for the high-priority OB and add a global "in use" semaphore (set/reset on a BOOL in M).

Watchdog on Slow CPUs

CPU 312 (the smallest S7-300) executes a single PEEK_DWORD call in approximately 35 µs. A 30 × 3 = 90 call sequence plus the loop overhead is ≈ 3.2 ms. The default OB1 watchdog on CPU 312 is 150 ms, so a single call is safe. If the FC is invoked from a 10 ms cyclic OB (OB35), the watchdog must be re-checked; on CPU 312 the budget is tight and the project will require either a faster CPU or a lower call frequency. The cycle-time table for the relevant CPUs:

CPU Bit op (µs) Word op (µs) DINT fixed-point (µs) Default OB1 watchdog
CPU 312 0.2 0.4 5 150 ms
CPU 314 0.1 0.2 2 150 ms
CPU 315-2 PN/DP 0.05 0.1 0.8 150 ms
CPU 317-2 PN/DP 0.025 0.05 0.4 150 ms
CPU 319-3 PN/DP 0.01 0.02 0.02 150 ms

Byte-Order on PEEK_DWORD

PEEK_DWORD reads in the S7-300 byte order. The result is a DWORD that the CPU treats as a 32-bit integer. Cast to DINT for signed arithmetic; the bit pattern is preserved.

Compound Operators

The S7-300/400 SCL compiler does not support +=, -=, *=, or /=. Use the long form iSum := iSum + dVal;. The long form compiles on every S7-300 CPU firmware V3.x and avoids the type-coercion rules that newer SCL versions apply to the compound form.

Verification Procedure

  1. Open PLCSIM V16 and start the project with the FB_Sum30DBs instance called from OB1.
  2. Open a watch table, force DB1.DBD100 = 1 000 000, DB15.DBD200 = 2 147 483 647 (max DINT), DB30.DBD300 = −2 147 483 648 (min DINT), and the remaining 87 DBDs = 0.
  3. Single-scan OB1 (PLCSIM: Execute → "Single scan") so the loop runs exactly once.
  4. Read iSum from the instance DB. Expected: 1 000 000 + 2 147 483 647 − 2 147 483 648 = 999 999.
  5. Repeat the test with all 90 DBDs = 1 000 000. Expected: 90 000 000.
  6. On a live CPU, use "Monitor / Modify" with the watch table and tick "Update" every 200 ms to see the running sum.

Online diagnostics that confirm the PEEK path is healthy:

  • SF (System Fault) LED off – no OB121 / OB122 loaded.
  • Diagnostic buffer entry "No faults" after the test.
  • The instance DB shows iSum equal to the expected scalar.

HMI Display of the Result

Once iSum is updated, an HMI tag is required to surface it on a WinCC (TIA) panel. Configure the tag in the HMI tags table with the following parameters:

Parameter Value
Name Tag_iSum
Connection S7-300/400 (default for the configured PN interface)
PLC tag FB_Sum30DBs_DB.iSum (instance DB of FB_Sum30DBs)
Data type DInt (32-bit signed)
Acquisition mode Cyclic, 500 ms
Display Decimal, signed, 0 fractional digits

For trending, add a tag log with a 1 s acquisition cycle and a 24 h retention; the PEEK loop runs on every OB1 scan, so the 1 s log rate avoids redundant data without missing peak values.

Alternative: ARRAY of UDT Instances

If the 30 DBs were created as a side effect of an early design choice, the architectural fix is to consolidate them into a single DB with an array of a UDT:

TYPE UDT_Meas
    STRUCT
        ValueAt100  : DINT;   // offset 0 inside the struct
        ValueAt200  : DINT;   // offset 4
        ValueAt300  : DINT;   // offset 8
        // ...
    END_STRUCT
END_TYPE

DATA_BLOCK DB_Sum
    STRUCT
        Channels : ARRAY[1..30] OF UDT_Meas;
        Total    : DINT;
    END_STRUCT
END_DATA_BLOCK

With this layout, the sum is a 30-iteration loop over DB_Sum.Channels[i].ValueAt100 + .ValueAt200 + .ValueAt300, no PEEK needed, no absolute offsets, and the absolute-addressing flag is no longer required. The PEEK pattern is the right answer when the 30 DBs already exist and cannot be consolidated, which is the situation that motivated the original question.

Migration Note: S7-1500 Path

S7-1500 SCL accepts the same PEEK function with a tighter type system, but the recommended approach on that platform is the symbolic slice access. The 30-DB problem reduces to a single FOR loop over an ARRAY of PLC data types, and PEEK is reserved for the rare case of accessing a DB whose structure is not known to the compiler (for example, a hand-off DB populated by an external tool). If the project is on the upgrade path, plan the migration of the 30 DBs to a single array-DB in the same window as the controller change. The TIA Portal V16 project migrator preserves the 30 DBs and converts the PEEK calls verbatim, but the symbolic access path is the long-term target.

Why does PEEK_DWORD need a non-optimized DB on S7-300?

PEEK_DWORD addresses memory by absolute byte offset, and S7-300 only supports the absolute-addressing (non-optimized) DB model. Optimized access with symbolic-only addressing was introduced with S7-1500 and is not available on the S7-300 CPU family; the PEEK call cannot be compiled against an optimized block even if the editor does not flag it.

Can I use PEEK on DB numbers above 255?

Yes. PEEK takes a WORD for the dbNumber parameter, so the legal range is 1 to 65 535. The S7-300 CPU family supports DB numbers 1..32 767 in standard mode. Confirm the bound in the CPU's technical data sheet (the upper bound varies by work memory size, but 32 767 is the practical ceiling for V3.x firmware).

Does the SCL FOR loop generate STL under the hood?

Yes. The S7-300 SCL compiler emits STL; the FOR loop becomes a counter in the local data area, an increment / compare / conditional branch, and a PEEK_DWORD call per array index. The compiled STL is functionally equivalent to a hand-written register-indirect loop and runs in the same number of CPU cycles, plus the call/return overhead of the PEEK function block.

What happens if a DB does not exist in the CPU?

PEEK_DWORD does not validate DB presence. The CPU raises OB121 (programming error) at the first instruction that addresses the missing DB. If OB121 is not loaded, the CPU goes to STOP. Load a stub OB121 in the project tree to keep the CPU in RUN while the data is filled in, and have the OB increment a non-retentive error counter in the instance DB so the diagnostic buffer reflects the misconfiguration.

Is the "+=" operator available in S7-300 SCL?

No. The compound assignment operators (+=, -=, *=, /=) are S7-1500 SCL additions and are not in the S7-300/400 SCL compiler. Use the long form: iSum := iSum + dVal;. The long form compiles on every S7-300 CPU firmware V3.x and avoids the implicit-type-conversion rules that newer SCL versions apply to the compound form.

Back to blog