Overview
Accessing data spread across multiple data blocks (DBs) without flooding the program with repetitive OPN instructions is a recurring challenge when programming Siemens S7-300, S7-400, and S7-1500 controllers. This reference explains the POINTER data type family defined for these platforms, the bit-level structure of each variant, and the practical STL/SCL patterns that allow an FC/FB to operate on a DB number supplied by the caller.
The information consolidates details from the official Siemens documentation entry covering POINTER for S7-300/S7-400/S7-1500 in TIA Portal V20 and the legacy STEP 7 Professional V14 SP1 manual. Engineers porting existing STEP 7 code to TIA Portal will find the bit structures, formal-parameter rules, and call-side assignment syntax reconciled here.
POINTER Data Type Variants in S7
Three pointer formats exist in the S7 language definition. Each variant is selected implicitly when a block parameter is declared.
| Variant | Length | Carries DB Number? | Typical Declaration | Typical Usage |
|---|---|---|---|---|
| Area (intra-area) pointer | 32 bits | No | POINTER | Implicit load/store of any address inside the currently opened DB or in bit memory/inputs/outputs |
| DB pointer | 48 bits | Yes (DB number in word) | POINTER | Formal parameter of FB/FC passing a DB address such as DB100.DBW100
|
| ANY pointer | 80 bits | Yes (DB number, data type, count) | ANY | Formal parameter passing entire arrays or structures with type information |
All three are visible in the TIA Portal block interface under Data types → Pointer and ANY. The 32-bit form is what remains after the DB number is stripped; it is never used as a block-parameter type because the receiver has no way to recover the DB context.
POINTER Bit Structure
The 48-bit DB pointer layout is the canonical form when an operand like P#DB100.DBX 10.0 is used in STL or is typed at a formal parameter. Two words and a half-word encode the address.
The relevant encoding for the byte/bit fields follows the area-code table used by LAR1/TAR1 on S7-300/400. For an ANY pointer ten bytes are written contiguously in the following order:
| Byte Offset | Width | Field | Notes |
|---|---|---|---|
| Bytes 0..1 | WORD | 10h = ANY identifier | Constant |
| Bytes 2..3 | WORD | Data type code | 02h=BYTE, 04h=WORD, 06h=INT, 07h=DINT, 08h=REAL |
| Bytes 4..5 | WORD | Count of elements | 1 for non-array actuals |
| Bytes 6..7 | WORD | DB number | 0 if not in a DB |
| Bytes 8..11 | DWORD | Byte.bit + area code | Same encoding as DB pointer |
Constructing a POINTER in STL
A POINTER is just a 32-bit (area) or 48-bit (DB) integer; it can be loaded like any other double word. The address constant P#DBxxx.DBByte.Bit is the symbolic entry the editor accepts. The example below mirrors the original problem statement: selecting element index 10 in four parallel DBs.
// STL (STEP 7 Professional V14 SP1)
L 10 // desired array index
SLD 3 // multiply by 8 to get byte.bit spacing
T #temp_ptr // MD 100 for example
OPN DB 100
L DBD [AR1,P#0.0] // pointer as base
T DB100.DBD 40
OPN DB 101
L DBD [AR1,P#0.0]
T DB101.DBD 40
OPN DB 102
L DBW [AR1,P#0.0]
ITD
TAK
/D
T DB103.DBD 40
The repetition is still required because the CPU opens only one DB at a time. The next sections show how to eliminate the repetition by changing the calling structure.
Constructing a POINTER in SCL
SCL (Structured Control Language, IEC 61131-3) hides the address arithmetic. Assigning DB100.DBW100 to a variable of type POINTER is sufficient; the compiler emits the 48-bit constant automatically.
// SCL inside an FB declared as POINTER
IF #IndexValid THEN
#pSrc1 := P#DB100.DBX 40.0; // initialize at first scan
END_IF;
#value1 := DWORD_TO_REAL(
IN:= WORD_TO_BLOCK_DB(100).DD[(#iIndex-1)*4 + 40/8]);
SCL is the recommended tool on TIA Portal V20 because STL is hidden by default and is not installed for new CPU projects targeting S7-1500. On S7-300/400, both editors remain available in STEP 7 Professional V14 SP1.
Block Parameter Passing for DB Pointers
The formal parameter declaration decides which pointer type is transferred. Use the rules below when designing FC/FB interfaces.
- Declare a formal parameter as
POINTERif the caller hands over a single DB operand (for exampleP#DB100.DBX 10.0). The full 48-bit pointer (including DB number) is copied into the temporary variables of the called block. - Declare the parameter as
ANYwhen arrays or structured operands are passed. The block receives an 80-bit descriptor containing the type code, length, DB number, and start address; no OPN is needed inside the called block. - Declare the parameter as
VARIANT(TIA Portal V14+) for polymorphic sources. Conversion toPOINTERorANYis possible inside the block.
Workaround: FB with Instance DB (Multi-Instance Pattern)
If the design requires the same index applied to four parallel arrays, the most compact implementation uses a single FB whose instance data block contains the four arrays. The data move is performed once during the parent FB call - the array elements are copied to the instance, and the FC/FB body accesses only the instance. No OPN, no POINTER arithmetic, no per-call block parameter passing for the arrays.
// FB declaration (TIA Portal V20 / STEP 7 V14 SP1)
FUNCTION_BLOCK MultiArrayFB
VAR_INPUT
iIndex : INT; // user-provided array index
END_VAR
VAR
aSource1 : ARRAY[1..200] OF REAL; // mirror of DB100.DBD40 region
aSource2 : ARRAY[1..200] OF REAL; // mirror of DB101.DBD40 region
aSource3 : ARRAY[1..200] OF INT; // mirror of DB102.DBW20 region
aTarget : ARRAY[1..200] OF REAL; // mirror of DB103.DBD40 region
END_VAR
VAR_TEMP
rVal1 : REAL;
rVal2 : REAL;
iVal3 : INT;
rProduct : REAL;
END_VAR
BEGIN
// Pull the values from the four DBs at the requested index
rVal1 := WORD_TO_BLOCK_DB(100).DD[((#iIndex-1)*4) + 40];
rVal2 := WORD_TO_BLOCK_DB(101).DD[((#iIndex-1)*4) + 40];
iVal3 := WORD_TO_BLOCK_DB(102).DW[((#iIndex-1)*2) + 20];
// Required math: rVal1 / rVal2, then convert iVal3 to REAL, divide again, write back
rProduct := rVal1 / rVal2;
rProduct := rProduct / DINT_TO_REAL(INT_TO_DINT(iVal3));
WORD_TO_BLOCK_DB(103).DD[((#iIndex-1)*4) + 40] := REAL_TO_DWORD(rProduct);
END_FUNCTION_BLOCK
The four WORD_TO_BLOCK_DB(...).DD[] expressions carry the DB context implicitly; no OPN is needed because TIA Portal generates view-of-DB references via the integrated instance DB or via multi-instance background. The call site shrinks to:
"InstanceDB".MultiArrayFB(iIndex := 10);
Why the Source Pattern Fails Without Restructuring
The original code loads from DB100 and DB101 in immediate sequence. The S7 CPU has only two address registers (AR1/AR2) and one DI/DB pair held in registers. You can read from DI (instance DB) and DB simultaneously on S7-400/1500, but a third concurrent DB read still requires an OPN or a manual AR manipulation. This is why each additional DB forces a sequential access. Lifting the data into an instance DB sidesteps the limitation entirely.
TIA Portal V20 vs STEP 7 Professional V14 SP1 Differences
| Aspect | TIA Portal V20 | STEP 7 Professional V14 SP1 |
|---|---|---|
| STL editor | Optional, installed as a separate package; not for S7-1200/1500 new projects | Always present in the LAD/FBD/STL editor |
SCL VARIANT parameter |
Supported | Supported from V14 SP1 onwards |
| POINTER P# constant | Same syntax; e.g. P#DB100.DBX 10.0
|
Identical |
| ANY with STRUCT/ARRAY OF STRUCT | Fully supported; length and type code emitted automatically | Fully supported |
| Block parameter assignment at call | Type info dialog; no P# prefix required per official docs | Type info dialog; identical behaviour per V14 SP1 manual |
S7-300/400/1500 Implementation Notes
-
S7-300: POINTER/ANY types use the standard 32/48/80-bit encoding. STL is fully available. AR1/AR2 cannot point into a DB whose DB number has not been opened - the assignment
P#DB200.DBX0.0accepted by AR1 stores the DB number but the CPU does not switch the DB register; you still needOPN DB[AR1]to dereference. - S7-400: Two DB registers are accessible simultaneously (DB and DI). Useful when reading from an instance DB (DI) and a global DB (DB) at the same time. Combined with a parameterised POINTER, an FB can dereference the source on DB and the instance on DI without explicit OPN at every call.
-
S7-1500: Optimised block access hides the POINTER representation - tags are referenced symbolically and the compiler embeds the address literal. The DB number is preserved implicitly. Area pointers and DB pointers still exist for legacy blocks but should be avoided in new projects; use
VARIANT,ANY, or symbolic tags instead.
Limits, Hazards, and Edge Cases
- DB number zero. A POINTER with DB number 0 references bit memory (M area). The area code byte 10001b (21h) means M. Without an explicit check, accidental assignments can land in the wrong area.
- Bit boundary at byte 65535. Byte offset is stored in bits 31..3 as a 29-bit value. Valid range 0..0x1FFFF (524287 bytes). Do not exceed the per-DB limit (8192 bytes standard, up to 65535 bytes for large DBs configured in CPU properties).
-
Optimised access on S7-1500. POINTER-typed parameters are not allowed when the block has optimised access; switch the block to standard (non-optimised) access, or use
VARIANT. -
Multi-instance depth. Recursive use of instance DBs compiles on S7-1500 only when each level has a unique qualifier (the editor assigns
Static_1,Static_1.Static_2...). Keep nesting < 8 levels to avoid online watch latency. -
AR1/AR2 volatile on FC calls. Save the pointer across a nested FC call with
LAR1 / TAR1or - preferred - use the multi-instance pattern that depends on the compiler-managed ARs only inside the FB scope.
Best Practices
- Reorganise related arrays into a single DB whenever cross-index math is repeated more than ten times. Locate the start address of each array at a known offset (commonly 0) so the index formula becomes a constant array multiplication.
- If the four DBs must remain separate, declare a multi-instance FB that holds copies of the four arrays. The "copy on first scan" pattern ensures the data block layout stays external to the function block.
- Use SCL
P#DBxxx.DByyy zconstants only at static locations; never build them dynamically inside loops unless the project is restricted to S7-400/1500 with non-optimised access. - Declare block parameters that receive DB addresses as
VARIANTfor new code, and cast toPOINTERorANYinside the block when the target S7-300/400 cannot be upgraded. - Always validate the received DB number inside the called block before dereferencing - it might point to a DB that is not loaded; protect with a sentinel like
ANYwith type code 0xFFFF to abort.
Verification Procedure
To verify the resulting program, follow these checks in online mode with the programming device connected:
- Insert the FB into OB1 with a fixed index (e.g. iIndex = 5) and the four source DBs loaded into the active project.
- Trigger a single scan and inspect the instance DB tag values for
aSource1[5],aSource2[5],aSource3[5]andaTarget[5]. They must equal the expected math result. - Repeat with iIndex = 200 (last element) to confirm the byte-offset arithmetic does not overflow into adjacent tags.
- Open the block consistency check (TIA Portal: Project > Compile > Software (rebuild all)) and confirm zero warnings related to POINTER or ANY parameter types.
- For S7-1500 targets, switch the block to optimised access and ensure the cross-DB parameter passing still resolves without compiler errors; if errors appear, revert to standard access for that block only.
Can I read from two DBs at the same time without an OPN?
Yes - on S7-400/1500 you can use the two DB registers (DB and DI). Pass the second DB via a POINTER formal parameter (DB number auto-loaded by the CPU), then dereference AR1 with L DIB [AR1,P#0.0] while the instance DB stays open. S7-300 has only one DB register, so an OPN is still required for a third DB.
Why does the editor strip "P#" at the call site?
Per the official POINTER documentation, the actual parameter is entered as e.g. DB100.DBW100; the compiler automatically inserts the internal P# encoding into the pointer. Typing P# manually in the call is illegal and produces an error.
How do I copy a structured array instead of a single variable?
Declare the formal parameter as ANY. The block receives the full descriptor (data type, length, DB number, byte offset). Inside the block, use SCL's ARRAY[*] slice or STL's BLOKMOV (SFC 20) to copy without any per-element pointer arithmetic.
What is the maximum DB number I can store in a DB pointer?
DB numbers 1..65535 fit in the 16-bit field of bytes 6..7 of an ANY pointer (or the high word of a 48-bit DB pointer). DB number 0 refers to the bit-memory area and is reserved for non-DB operands.
Should I use POINTER or VARIANT in new code?
Use VARIANT in new code. VARIANT is available from STEP 7 V14 SP1 and TIA Portal V14 onwards; it allows polymorphism across all data types and can be inspected with TypeOf() / IS_NULL(). POINTER remains valid for legacy blocks and for parameter passing into S7-300/400 STL where the editor does not surface VARIANT cleanly.