Step7 BLOCK_DB and ANY Parameters: Runtime Construction Guide

David Krause17 min read
SiemensTechnical ReferenceTIA Portal
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

Siemens STEP 7 and TIA Portal expose two parameter types that are essential when writing generic, reusable function blocks: BLOCK_DB and ANY. They look similar on the surface but serve very different purposes, and the interaction between them is the single most common stumbling block for engineers writing library FBs that need to operate on data residing in caller-selected data blocks.

A BLOCK_DB parameter is essentially a typed handle to a data block — it carries the DB number and a reference into the block descriptor. It can only point to a DB; it cannot be used as a memory address for byte-level read/write operations. An ANY parameter is a 10-byte area pointer describing a typed memory region (data type code, length, DB number, byte/bit offset). It is the only pointer type accepted by SFC20 BLKMOV, SFC21 FILL, SFC75 SET, and a long list of standard library FBs that perform bulk operations.

The real-world problem appears whenever a function block must accept a destination DB that is only known at call time, and then route data into that DB through an ANY-based system function. The classic editor does not allow you to pass a BLOCK_DB parameter where an ANY is expected, and it will not automatically synthesise the ANY structure for you. The solution is to construct the ANY pointer in a temporary variable at runtime and pass that constructed ANY into the downstream call.

Block parameter families in STEP 7: Per the TIA Portal parameter types reference, the BLOCK family covers BLOCK_FB, BLOCK_FC, BLOCK_DB, and BLOCK_SDB. Each accepts a block address as the actual parameter (e.g., DB3 for BLOCK_DB, FB16 for BLOCK_FB). They are not memory addresses and cannot be dereferenced like POINTER or ANY values.

How the Editor Constructs ANY Pointers Behind the Scenes

When you assign a symbolic variable (static, temp, or hard-coded address) to an ANY input of an FC, the STEP 7 / TIA editor silently emits code that builds the 10-byte ANY structure in the local stack. The 10 bytes are laid out as follows:

Offset Width Field Notes
+0 WORD Syntax ID / Data type code 0x10 = BYTE, 0x1002 = BYTE array, 0x1005 = INT, 0x1008 = REAL, 0x100B = BOOL, 0x100C = WORD, 0x100D = DWORD
+2 WORD Number of elements (length) Bytes for byte-type, count for non-byte types
+4 WORD DB number 0 for non-DB areas
+6 DWORD Area pointer (byte.bit) Bit 24..31 = area ID; 0x81 = DB, 0x82 = DI (instance DB), 0x83 = local, 0x84 = M, 0x85 = I, 0x86 = Q, 0x87 = L

Four canonical examples illustrate the emitted code paths. Each is followed by the editor-generated CALL expansion in STL/MC7 so the runtime construction is visible.

Example 1 — ANY from FB static variable

The source call is:

CALL  FC 2
   pAny := #StaticVarReal;
   NOP  0;

Behind the scenes, the editor writes the ANY to LW 21 and passes P#L 21.0 to the FC. The instance DB number is taken from DINO, and the static offset is added to AR2 to form the absolute area pointer.

CALL
   BLD   1
   =     L     20.0
   TAR2  LD   16           //save AR2 in local data
   L     W#16#1008        //type 8 = REAL
   T     LW   21
   L     1                //count = 1
   T     LW   23
   L     DINO             //current instance DB
   T     LW   25
   +AR2  P#2.0            //add offset of StaticVarReal
   TAR2                   //absolute area pointer
   LAR2  LD   16           //restore AR2
   T     LD   27           //ptr address into ANY area
   UC    FC   2
         P#L 21.0          //any pointer at L21.0
   LAR2  LD   16
   BLD   2
END_CALL
   NOP   0;

Example 2 — ANY from FB temp variable

Same skeleton, but DB number = 0 because temp data lives in the L stack.

CALL
   BLD   1
   =     L     20.0
   TAR2  LD   16
   L     W#16#1008
   T     LW   21
   L     1
   T     LW   23
   L     0                //DB number = 0 for local
   T     LW   25
   L     P#L 2.0          //area pointer to TempVarReal
   T     LD   27
   UC    FC   2
         P#L 21.0
   LAR2  LD   16
   BLD   2
END_CALL
   NOP   0;

Example 3 — ANY from hard-coded DB address

CALL  FC 2
   pAny := P#DB99.DBX 0.0 INT 33;
   NOP  0;

The editor emits a length of 33 (INT count) and DB number 99 with area ID 0x81.

CALL
   BLD   1
   =     L     20.0
   TAR2  LD   16
   L     W#16#1005        //type 5 = INT
   T     LW   21
   L     33               //count = 33
   T     LW   23
   L     99               //DB99
   T     LW   25
   L     P#DBX 0.0        //area pointer
   T     LD   27
   UC    FC   2
         P#L 21.0
   LAR2  LD   16
   BLD   2
END_CALL
   NOP   0;

Example 4 — ANY from a temp variable of type ANY

This is the critical case for our use. When the actual parameter is a temp ANY variable, the editor emits no construction code. It assumes you have already written the 10 bytes into that temp location before the call.

CALL
   BLD   1
   =     L     20.0
   TAR2  LD   16
   UC    FC   2
         P#L 6.0           //editor trusts you filled L6.0..L15.7
   LAR2  LD   16
   BLD   2
END_CALL
   NOP   0;

This is the door the runtime construction pattern walks through: declare a temp ANY, fill it yourself with LAR1 P##var and a few loads/stores, and pass it as the actual to the downstream block call.

Constructing an ANY Pointer at Runtime from a BLOCK_DB

The pattern requires five ingredients:

  1. A BLOCK_DB input parameter carrying the destination block from the caller.
  2. An INT input for the byte count (or element count, depending on the chosen syntax ID).
  3. A TEMP variable of type ANY (this is the variable the editor will pass through unchanged).
  4. An AR1 setup pointing at the start of the temp ANY.
  5. Open the destination DB with OPN #DestDB before the SFC/FB call so the runtime resolves the area correctly.

The reference FB from the source — used by the engineer to test SFC21 FILL through a runtime-built ANY — is reproduced here with the field-tested comments restored.

FUNCTION_BLOCK FB 2
TITLE =
VERSION : 0.1

VAR_INPUT
  DestDB          : BLOCK_DB;
  iNumberOfBytes  : INT;
END_VAR

VAR
  StaticData      : BOOL;
END_VAR

VAR_TEMP
  TempData             : BYTE;
  RunTimeCreatedAny    : ANY;
END_VAR

BEGIN
NETWORK
TITLE = Create a runtime ANY pointer
      LAR1  P##RunTimeCreatedAny;     //AR1 -> ANY structure
      L     W#16#1002;                //syntax ID: BYTE (array of bytes)
      T     W [AR1,P#0.0];
      L     #iNumberOfBytes;          //length in bytes
      T     W [AR1,P#2.0];
      OPN   #DestDB;                  //open the destination DB
      L     DBNO;                     //read active DB number
      T     W [AR1,P#4.0];            //store as DB number field
      L     P#DBX 0.0;                //area pointer, byte 0 bit 0
      T     D [AR1,P#6.0];

NETWORK
TITLE = Example - SFC21 FILL via the constructed ANY
      L     99;
      T     #TempData;
      CALL  "FILL" (
            BVAL    := #TempData,
            RET_VAL := MW 0,
            BLK     := #RunTimeCreatedAny);
END_FUNCTION_BLOCK

Key behavioural points:

  • P##RunTimeCreatedAny resolves at runtime to the absolute local-stack address of the temp ANY, including the 0x83 area identifier for L-stack data. The compiler will not let you use this on a non-temp variable.
  • OPN #DestDB opens the destination DB so subsequent L DBNO reads the correct number. Without this, the ANY will still hold DB 0 (or whatever was previously open) and SFC21 will return W#16#8091.
  • P#DBX 0.0 places the area ID 0x81 in the high byte of the area pointer DWORD, which is what SFC20/SFC21 expect for a DB area.
  • You may freely change the syntax ID. Use W#16#1002 for byte arrays, W#16#1005 for INT, W#16#1008 for REAL, W#16#100B for BOOL, etc. The length field then becomes element count, not byte count, except for type 0x10 (BYTE) and 0x1002 where it is byte count.

Step-by-Step Implementation of CopyByte

The original question asks for an FB that copies a configurable number of bytes from an ANY source to a destination whose DB number is only available as a BLOCK_DB. The implementation below uses SFC20 BLKMOV rather than the byte-by-byte loop, which is the practical way to copy more than a few bytes at a time.

FUNCTION_BLOCK FB 100
TITLE = 'CopyByte - generic copy via runtime ANY'
VERSION : 1.0

VAR_INPUT
  Source          : ANY;        //caller supplies source ANY
  DestDB          : BLOCK_DB;   //caller supplies destination DB
  NumberOfBytes   : INT;        //byte count, 1..N
  Error           : INT;        //SFC20 RET_VAL echoed here
END_VAR

VAR_TEMP
  SourceAnyCopy   : ANY;        //mirror of input for static usage
  DestAny         : ANY;        //constructed destination ANY
END_VAR

BEGIN
NETWORK 1
TITLE = Mirror source ANY into temp (input ANYs are read-only on first scan in some FBs)
      LAR1  P##SourceAnyCopy;
      L     P##Source;
      LAR2 ;
      L     W [AR2,P#0.0];  T W [AR1,P#0.0];
      L     W [AR2,P#2.0];  T W [AR1,P#2.0];
      L     W [AR2,P#4.0];  T W [AR1,P#4.0];
      L     D [AR2,P#6.0];  T D [AR1,P#6.0];

NETWORK 2
TITLE = Build destination ANY from DestDB input
      LAR1  P##DestAny;
      L     W#16#1002;                //BYTE
      T     W [AR1,P#0.0];
      L     #NumberOfBytes;
      T     W [AR1,P#2.0];
      OPN   #DestDB;
      L     DBNO;
      T     W [AR1,P#4.0];
      L     P#DBX 0.0;
      T     D [AR1,P#6.0];

NETWORK 3
TITLE = Execute SFC20 BLKMOV
      CALL  "BLKMOV" (
            SRCBLK  := #SourceAnyCopy,
            RET_VAL := #Error,
            DSTBLK  := #DestAny);
END_FUNCTION_BLOCK

Calling the FB looks like this in a higher-level OB or FB:

CALL  FB 100, DB 200
      Source        := P#DB10.DBX 0.0 BYTE 16,
      DestDB        := DB 60,
      NumberOfBytes := 16,
      Error         := MW 100;

Sixteen bytes are copied from DB10 into DB60. Replace DB60 with DB61, DB62, or DB63 depending on the Cognex station that triggered the call.

Routing Data into the Correct Station DB (Cognex Case Study)

The follow-up question describes fetching supplementary data from a Cognex camera on one of four stations, and storing the result in DB60, DB61, DB62, or DB63 depending on which station the camera is mounted on. The pattern is a textbook runtime-ANY application.

  1. Determine the destination station with a small piece of logic that maps a station number (1..4) to a DB number (60..63). Avoid B#16#0 for the DB number field at any time — SFC20 rejects DB 0 with W#16#8090.
  2. Pass the computed DB number into the DestDB input of the CopyByte FB as a BLOCK_DB actual parameter, e.g., DestDB := DB [StationNumber + 59]. The square-bracket indexed access is supported on BLOCK_DB parameters in SCL and STL.
  3. Re-open the destination DB inside the FB with OPN #DestDB before reading DBNO. This is required even if the caller opened it; the FB cannot assume the DI/DB registers survived the call.
  4. Set the ANY syntax ID and length to match the data being moved. For a 32-byte Cognex status record the values are W#16#1002 and 32; for a structure of five REALs use W#16#1008 and 5.
  5. Evaluate the Error output of the FB and propagate to the caller's error word. The SFC20 RET_VAL values most often seen in this scenario are listed in the troubleshooting matrix below.
Indexed BLOCK_DB access: DB [n] in STL or DB_NUMBER in SCL resolves to a BLOCK_DB value at runtime and is the only way to forward a variable DB number into a BLOCK_DB formal parameter in classic STEP 7. In TIA Portal V15 and later, you can also use "DB_name" with array-of-DB techniques, but the runtime OPN + DBNO pattern still applies.

Restrictions on Passing BLOCK_DB Parameters

Siemens KB article 8686787 — "Passing on parameters of the BLOCK_DB type" — is the authoritative reference for the rules and edge cases. The rules that affect the CopyByte pattern are:

Call topology BLOCK_DB pass-through Allowed?
OB → FB1 → FC (BLOCK_DB formal → actual) Direct Yes
FB1 → FB2 (both have BLOCK_DB formal) Direct pass-through Yes
FB1 multi-instance → nested FB (different instance) Direct Yes (since S7-400 and S7-300 v3.x)
FC1 → FB2 (FC has BLOCK_DB formal) Direct Yes (FC formally)
Any block → SFC/SFB with BLOCK_DB formal Direct Generally yes, but check the SFC signature
Indirect DB access using DB [n] Passes runtime value to ANY construction Yes (recommended pattern)

The classic failure is the S7-300 v2.x restriction: passing a BLOCK_DB parameter into another block's BLOCK_DB formal was not supported on S7-300 CPUs below firmware V3.0. The pattern is fully supported on every S7-300/400, S7-1500, and WinAC controller that STEP 7 V5.x and TIA Portal V13+ target. On ET200S IM151 CPUs, consult the CPU-specific manual because the smallest variants sometimes block the OPN DI variant.

The TIA V14 BLOCK_DB data type thread is also worth scanning for a real-world failure mode: the OPN instruction on an in-out BLOCK_DB formal sometimes compiles cleanly in TIA V14 but generates the wrong AR/DB combination when the calling FB is a multi-instance, because AR2 is already pointing at the parent instance. Always save and restore AR2 around the OPN if the calling context is a multi-instance, and prefer LAR1 P##DestAny over the implicit-pointer path.

Syntax ID and Area ID Reference

Pick the syntax ID and area ID that match the SFC/FB you are calling. Mismatches yield W#16#8092 (length error) or W#16#8094 (alignment error) from SFC20.

Data type Syntax ID Length field means
BOOL W#16#0001 (bit) or W#16#1001 (array) bit count or element count
BYTE W#16#0002 (bit) or W#16#1002 (array) byte count
CHAR W#16#1003 element count (1 char = 1 byte)
WORD W#16#1004 element count (1 word = 2 bytes)
INT W#16#1005 element count
DWORD W#16#1006 element count
DINT W#16#1007 element count
REAL W#16#1008 element count
TIME / DATE / TOD / S5TIME W#16#1009 .. W#16#100F element count

Area ID byte in the high byte of the area pointer DWORD:

Area Area ID Use with
DB 0x81 P#DBX x.y
DI (instance DB) 0x82 Multi-instance context
L (local / temp) 0x83 Temp variables of containing block
M (merker) 0x84 Bit memories
I (process input image) 0x85 Inputs
Q (process output image) 0x86 Outputs

Verification and Commissioning

  1. Online ANY inspection. Open the FB in online mode, place the cursor on the temp RunTimeCreatedAny, and use Monitor/Modify on the L stack. The 10 bytes must read 1002 0010 003C 81 00 00 00 for a 16-byte copy into DB60 starting at byte 0. (Hex: 1002 = type byte-array, 0010 = 16 bytes, 003C = DB60, 81 00 00 00 = area DB, offset 0.0.)
  2. SFC20 RET_VAL check. Force the destination DB to be smaller than NumberOfBytes. The expected W#16#8091 confirms the runtime construction placed the correct DB number in the ANY.
  3. Step-by-step cycle. Set a breakpoint after OPN #DestDB and verify DBNO matches the caller-supplied DB. Inconsistent DBNO indicates a missing OPN or an instance-DB confusion in a multi-instance context.
  4. Length cross-check. Add an Error = 0 branch that sets a flag if NumberOfBytes > 8192 (the SFC20 length cap for non-byte types) or if NumberOfBytes < 1. The SFC will accept length 0 with no error but the copy will be a no-op — easy to mistake for a logic bug.
  5. Station routing test. Cycle through stations 1..4 and confirm the destination DB increments DBNO between calls. Use VAT or HMI tag to read DBNO directly after the OPN.

Troubleshooting Matrix

Symptom Probable cause SFC20 RET_VAL Fix
RET_VAL = W#16#8090 ANY references DB0 or area mismatch W#16#8090 Ensure OPN #DestDB ran before L DBNO; the high byte of area pointer must be 0x81 for DB area.
RET_VAL = W#16#8091 Length exceeds destination DB W#16#8091 Cap NumberOfBytes at the destination DB length; use DB_LENGTH intrinsic if available in TIA V16+.
RET_VAL = W#16#8092 Source and destination areas overlap illegally W#16#8092 Use two non-overlapping ranges; for in-place ops, split the move.
RET_VAL = W#16#8093 Alignment error (e.g., WORD at odd byte offset) W#16#8093 Use syntax ID 0x1002 (BYTE) for byte-granular moves; switch to 0x1004 (WORD) only on word-aligned offsets.
RET_VAL = W#16#80B1 Source DB does not exist W#16#80B1 Confirm the source ANY carries the correct DB number; often caused by reading a stale DI in a multi-instance FB.
Data appears scrambled in destination Wrong syntax ID (e.g., REAL declared as INT) n/a Match syntax ID to the actual data layout: 0x1008 for REAL, 0x1005 for INT, 0x1002 for BYTE.
No error but zero bytes copied Length field 0 or 1 with bit syntax ID n/a Use byte-array syntax ID 0x1002 with explicit byte count.
SF LED on CPU after first call Stack overflow from too many temp ANYs n/a Consolidate to one temp ANY per FB; declare locally, not as STATIC, to release L-stack on FB exit.
TIA V14 compiles but OPN jumps to wrong DB at runtime Multi-instance AR2 pointer conflict n/a Save/restore AR2 around the OPN; avoid in-out BLOCK_DB in multi-instance FBs in TIA V14 SP1.

Edge Cases and Field-Proven Caveats

  • Optimised block access. In TIA Portal with "optimised block access" on the destination DB, the SFC still works because the ANY uses DB number + byte offset. The optimisation is internal to the compiler; it does not change the runtime area ID.
  • Type ANY on a function (FC) input. When the ANY is on the input of a downstream FC, the editor will again synthesise code. Pass the temp RunTimeCreatedAny as the actual parameter and let the editor walk through Example 1's expansion at the new call site.
  • Calling a multi-instance FB. In a multi-instance context, the instance DB register (DI) is the multi-instance parent, not the one you OPN'd. If you are copying into a different DB inside a multi-instance FB, you must re-OPN to the destination DB before any pointer arithmetic that uses DBNO. The AR2 register holds the instance base, and operations like +AR2 P#x.y resolve against that instance — do not mix this with the destination DB offset.
  • Length overflow with REAL syntax ID. Using 0x1008 and length 5 means five REALs = 20 bytes. The SFC uses the syntax ID to scale, not to validate. If you supply length 5 and source only has one REAL, the read will bleed past the DB boundary and the CPU will enter STOP with a DB length error on the source side.
  • Symbolic ANY vs. absolute ANY. TIA Portal SCL accepts P#"MyDB".StaticStruct as an ANY literal. At compile time the symbolic reference is resolved to a byte offset; at runtime the resolved value is the offset. You cannot use a symbolic reference inside a runtime-constructed ANY — you must compute the byte offset symbolically (e.g., DB_OFFSET_OF("MyStruct") in SCL) and use that as the area pointer.
  • 32-bit count for large moves. If the move is more than 32 KB, replace INT with DINT for the byte count and load the DINT into the W field with a manual split. SFC20 supports up to the per-call maximum of the CPU (e.g., 64 KB on S7-1500, 16 KB on S7-315, 32 KB on S7-416).

Alternative Implementations in SCL

The same logic in SCL is significantly shorter and easier to maintain. The runtime-construction of the ANY is hidden behind the MOVE_BLK / FILL_BLK builtin if the destination is reachable symbolically, but for variable DB targets the OPN / L DBNO idiom is still required.

FUNCTION_BLOCK "CopyByteSCL"
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : AUTHTECH
FAMILY : LIB

VAR_INPUT
  Source         : VARIANT;     //or ANY, but VARIANT is TIA V14+
  DestDB         : BLOCK_DB;
  NumberOfBytes  : INT;
END_VAR
VAR_OUTPUT
  Error          : INT;
END_VAR
VAR_TEMP
  info           : VARIANT_INFO; //TIA V15.1+ for length introspection
  destAny        : ANY;
END_VAR
BEGIN
  // Build the destination ANY
  destAny := DWORD_TO_ANY(IN := DW#16#1002_0010_0081_0000);
  // Note: the DWORD layout is bytes 6..9 (area pointer) in the low DWORD of the
  // 10-byte structure, with DB number fixed at runtime by OVERWRITE below.
  // For TIA V16+ a cleaner alternative is to use the *AnyBuilder helpers in
  // the LGF library, e.g. "LGF_AnyToDBDestination".

  OPN   #DestDB;
  destAny.dbNumber := DBNO;
  destAny.len       := UINT(#NumberOfBytes);

  Error := BLKMOV(
              SRCBLK := Source,
              DSTBLK := destAny,
              RET_VAL => Error);
END_FUNCTION_BLOCK

If Source is a VARIANT, the BLKMOV builtin (SFC20) accepts it directly. If Source is a typed ANY, the conversion is implicit. The SCL version is functionally equivalent to the STL version above and is the recommended style for new TIA Portal projects.

FAQ

Why does the STEP 7 editor reject passing a BLOCK_DB directly into an ANY input?

BLOCK_DB and ANY are separate data type families. BLOCK_DB is a typed handle to a data block (4 bytes: DB number + flags), while ANY is a 10-byte area pointer including syntax ID, length, DB number, and byte.bit offset. The editor cannot auto-coerce one into the other; you must build the ANY in a temp variable at runtime with the OPN #DestDB / L DBNO idiom.

Which syntax ID should I use for a byte-by-byte copy of N bytes?

Use W#16#1002 (BYTE array). The length field is the byte count, and the area pointer uses area ID 0x81 for DB. For INT or REAL arrays, switch to 0x1005 or 0x1008 and supply the element count, not the byte count, in the length field.

Can I pass the BLOCK_DB from one FB to another FB without rebuilding the ANY?

Yes, as long as both blocks use BLOCK_DB as the formal parameter type. The runtime-construction step only happens at the call into SFC20/SFC21 or any other block that requires an ANY. The Siemens KB article 8686787 documents the supported pass-through topologies and the S7-300 V2.x exception.

What does SFC20 RET_VAL W#16#8091 mean in this context?

The destination area is shorter than the requested byte count. The runtime-constructed ANY is correct — it points at the right DB and the right offset — but the destination DB does not have enough bytes after that offset to receive the move. Reduce NumberOfBytes or use a larger DB.

Does the pattern work in SCL with optimised block access?

Yes. Optimised block access changes how the compiler lays out the DB, but the runtime OPN / DBNO / SFC20 sequence still uses the DB number and byte offset, which are unaffected by optimisation. The SCL source from the alternative-implementations section compiles and runs on S7-1500 with optimised access enabled.

Back to blog