Accessing SCL Instance DBs from Function Blocks in TIA Portal

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

Function blocks (FBs) in Siemens S7-300, S7-400, S7-1200, and S7-1500 controllers store their static variables in a dedicated instance data block (DB). When you need to read or write a tag that lives inside that instance DB from a different FB, the CPU requires an unambiguous access path: either a symbolic name, an absolute address, a typed multi-instance, or a typed parameter interface. This reference covers every legal method, the syntax used in SCL (Structured Control Language), and the constraints that apply when working with library blocks such as the Siemens CEMAT V9 mineral processing library where adding a symbol name to every auto-generated instance DB is impractical.

Prerequisites

  • TIA Portal V16 or later (SCL V5.3+ behavior confirmed on V17, V18, V19, V20, V21).
  • STEP 7 Professional or SCL add-on installed.
  • Target CPU: S7-1200 (firmware ≥ V4.2), S7-1500 (any FW), or S7-300/S7-400 with classic STEP 7 V5.7.
  • Source FB compiled without errors and a single-instance or multi-instance DB present in the program.
  • Knowledge of the instance DB number (auto-assigned or manual) and the static variable name declared in the FB's VAR/VAR_INPUT/VAR_OUTPUT/VAR_IN_OUT/VAR_TEMP/VAR STATIC section.

Instance DB Concepts

When the SCL compiler generates a call to an FB, it creates (or re-uses) an associated instance DB that contains one memory image of every VAR, VAR_TEMP, VAR_INPUT, VAR_OUTPUT, and VAR_IN_OUT declared in the FB. Two storage models are supported:

Model Generated By DB Quantity Best Use Case
Single instance FB call in OB/FB/FC One DB per FB call Standalone FBs, library blocks (CEMAT drives, valves)
Multi-instance FB declared as Static inside parent FB Parent DB only Modular machine code with many sub-motors

The instance DB is the working memory of the FB. VAR_TEMP values are NOT stored in the instance DB; they live in the L stack of the calling OB. Only VAR/VAR_INPUT/VAR_OUTPUT/VAR_IN_OUT/VAR STATIC and VAR CONSTANT are persistent and addressable from other code.

Method 1 — Symbolic Access (Recommended)

The TIA Portal compiler creates an automatically generated symbol for every block; you can reference the instance DB either by the auto-symbol or by a user-defined name from the PLC tag table. From SCL this is the safest and refactor-proof form.

// Reader FB
"Motor_1_DB".SpeedSetpoint := 1500.0;     // Write static in another FB's instance DB
IF "Motor_1_DB".bRunning THEN             // Read static
    bSystemReady := TRUE;
END_IF;

To use this form without manually renaming every DB, open the PLC tag table, switch to the Names of the block view, and confirm the default symbol created by TIA. For an FB named MotorUni whose call you placed in OB1, the system assigns the default symbol "MotorUni_DB". You can call this from any SCL source file in the same program without touching the symbol table at all.

Why the questioner thought it was impractical: in legacy STEP 7 V5.x, each newly inserted block required a manual symbol entry in the symbol table (SYM_TAB) before DBxxx could be referenced symbolically. In TIA Portal V13+ the compiler populates the symbol table automatically, so you almost never need to type a symbol name yourself — just use the FB name + _DB suffix or the auto-symbol assigned to the call site.

Method 2 — Absolute Access (DBx.DBxxx or %DBx)

If the instance DB has no symbolic name and you know the absolute DB number plus the byte offset, you can read or write any static directly. The syntax is supported both in classic SCL and in TIA Portal SCL.

// Absolute read in SCL
rCurrentSpeed := DB20.DBD12;             // Real at byte offset 12 of DB20
bStartCmd      := DB20.DBX0.0;           // Bool at bit 0.0 of DB20

// IEC-style address (TIA Portal V15+)
rCurrentSpeed := %DB20.DBD12;
bStartCmd      := %DB20.DBX0.0;

Use the FB's Instance DB properties dialog to look up the DB number when the compiler assigned it. DB20 in the example above corresponds to a single-instance call of an FB such as FB1000 "CEMAT_MotorUni". Offsets map to the static variables in the order declared in the VAR section. The same offsets are visible in the Instance DB > Monitor/Modify > Data view of TIA Portal.

Caution: absolute access breaks the moment the FB declaration order is changed. Any new VAR inserted before existing variables shifts every subsequent offset, which means external code that wrote to DB20.DBD12 will silently target the wrong variable after a recompile. Use this form only for diagnostics, not for production logic.

Method 3 — Multi-Instance (FB Inside FB)

Multi-instance is the correct Siemens-blessed pattern for calling a library FB from a wrapper FB without proliferating instance DBs. Declare the library FB as a static variable of the wrapper; the compiler stores its static data inside the wrapper's own instance DB.

// Wrapper FB (SCL)
FUNCTION_BLOCK "DriveSupervisor"
VAR
    // Multi-instance: NO separate DB is generated for MotorUni
    Motor_A : "CEMAT_MotorUni";   // type = FB
    Motor_B : "CEMAT_MotorUni";   // second instance shares the parent DB
    iActive : INT;
END_VAR
BEGIN
    Motor_A.StartCommand := TRUE;
    Motor_A.SpeedRef     := 1450.0;
    Motor_A();                      // call the multi-instance
    Motor_B.StartCommand := FALSE;
    Motor_B();
    iActive := Motor_A.StatusWord;
END_FUNCTION_BLOCK

This is the cleanest way to consume CEMAT blocks because:

  • No symbol-name entry is required in the global symbol table.
  • All instance data is encapsulated in the parent DB.
  • Multiple instances of the same FB consume one DB instead of N DBs.

For multi-instance to compile, the Multiple Instance Capability attribute of the called FB must be set. The TIA Portal V21 SCL documentation describes this in the Single Instance and Multi-Instance chapter. All standard Siemens library FBs and CEMAT V8+ motor blocks are multi-instance capable.

Method 4 — Parameter-Passing (InOut / Input / Output)

If you only need to exchange a small number of tags, expose them on the interface of the called FB and forward them. This avoids the question of cross-DB access entirely.

FUNCTION_BLOCK "CEMAT_MotorUni"
VAR_INPUT
    StartCommand : BOOL;
    SpeedRef     : REAL;
END_VAR
VAR_OUTPUT
    StatusWord   : INT;
    bRunning     : BOOL;
END_VAR
// Caller FB
"Motor_1_DB"(StartCommand := bCmd, SpeedRef := rSpd, StatusWord => iStat, bRunning => bRun);

Parameter passing is type-safe, refactor-safe, and is the form the Siemens SCL programming and programming style guide recommends. The CEMAT motor FBs already provide StartCommand, StopCommand, SpeedRef, and StatusWord on their interface, so the cleanest design rarely needs cross-DB access at all.

Method 5 — Indirect DB Access with WORD_TO_BLOCK_DB

For diagnostic or HMI scenarios where the DB number is calculated at runtime, use the standard WORD_TO_BLOCK_DB / DB_ANY system functions. This is the only form that satisfies the questioner's literal request — "call an instance DB by block name, not symbol name" — when the block name itself is a variable.

FUNCTION "DiagReader" : Void
VAR_INPUT
    iDBNumber : INT;          // populated by HMI, for example
END_VAR
VAR_TEMP
    tDBInfo : STRUCT
        bIsInstance : BOOL;
        nBlockNum  : INT;
    END_STRUCT;
END_VAR
BEGIN
    IF iDBNumber = 0 THEN RETURN; END_IF;
    // Read using a symbolic alias stored in a tag-array-of-DB numbers
    CASE iDBNumber OF
        1 : rSpeed := "Motor_1_DB".SpeedRef;
        2 : rSpeed := "Motor_2_DB".SpeedRef;
        3 : rSpeed := "Motor_3_DB".SpeedRef;
        ELSE rSpeed := 0.0;
    END_CASE;
END_FUNCTION

For genuinely dynamic block-number access (rare; mostly for diagnostic repeaters), use:

// Dynamic read with ANY pointer
VAR_TEMP
    tDB : REF_DB;
END_VAR

tDB := WORD_TO_BLOCK_DB(iDBNumber);
IF tDB.NUMBER <> 0 THEN
    // Pass DB into a function that takes a VARIANT or ANY parameter
    rSpeed := tDB.DD[12];  // does NOT exist as direct syntax; use library FB "RDREC" or "GetSymbol" via API
END_IF;
Runtime symbolic access is restricted on S7-1200/S7-1500 for security reasons. Use the S7-1500 GetSymbol / GetSymbolName instructions from the "Diagnostics" palette, or expose the tag as a parameter to a wrapper FB instead of scanning every DB at runtime.

Method 6 — AT View on the Instance DB

Declare a PLC data type (UDT) that mirrors the layout of the target FB's static section, then assign an AT view of the instance DB to that UDT in the calling block. This is useful when the FB interface is locked (library-protected) and you need to inspect a private static for diagnostic purposes.

// UDT that mirrors the layout of CEMAT_MotorUni statics
TYPE "UDT_MotorUniView"
    STRUCT
        bStartCmd   : BOOL;
        bStopCmd    : BOOL;
        rSpeedRef   : REAL;
        iStatusWord : INT;
        bRunning    : BOOL;
    END_STRUCT
END_TYPE
// Calling FB
VAR
    MotorView AT "Motor_1_DB" : "UDT_MotorUniView";
END_VAR

MotorView.rSpeedRef := 1200.0;
IF MotorView.bRunning THEN ... END_IF;

The AT keyword binds a symbolic view to an existing DB without copying the data; the compiler generates plain DB-offset access behind the scenes. This is functionally identical to Method 2 but type-checked at compile time.

Comparison of Access Methods

Method Compile-Checked Refactor-Safe Requires Symbol Entry Performance Use When
Symbolic ("FB_DB".var) Yes Yes No (auto) Native load/transfer Default choice
Absolute DBx.DBxxx No No No Native load/transfer Diagnostic only
Multi-instance Yes Yes No Native load/transfer Encapsulation, library consumption
Parameter passing Yes Yes No Native load/transfer Library design, clean interfaces
WORD_TO_BLOCK_DB Partial No No Indirect HMI-driven diagnostics
AT view Yes Yes (if UDT matches) No Native load/transfer Reading library-protected statics

Working Code: CEMAT Motor Uni-Directional Drive Call

The following complete SCL snippet shows a wrapper FB that drives a unidirectional motor from the CEMAT library, accesses its instance statics, and uses parameter passing to expose the data to a higher-level sequencer. No symbol table entries are required beyond the auto-generated ones.

FUNCTION_BLOCK "PumpStationCtrl"
VAR
    // Multi-instance of CEMAT block — no separate DB is created
    Pump1 : "CEMAT_MotorUni";       // multi-instance, type = FB
    Pump2 : "CEMAT_MotorUni";

    // HMI-visible tags
    rSpd1 : REAL;
    rSpd2 : REAL;
    bRun1 : BOOL;
    bRun2 : BOOL;
END_VAR

BEGIN
    // Drive call 1
    Pump1.StartCommand := bRun1;
    Pump1.SpeedRef     := rSpd1;
    Pump1();
    bRun1 := Pump1.bRunning;

    // Drive call 2
    Pump2.StartCommand := bRun2;
    Pump2.SpeedRef     := rSpd2;
    Pump2();
    bRun2 := Pump2.bRunning;
END_FUNCTION_BLOCK

When this FB is called from OB1 as a single instance (e.g., "PumpStationCtrl_DB"), the compiler generates one DB whose layout includes the inlined statics of both Pump1 and Pump2. The HMI can monitor the upper-level DBs symbolically without ever knowing the inner CEMAT instance structure.

Troubleshooting Matrix

Symptom Likely Cause Fix
Compiler error: "Instance DB does not exist" Auto-assigned DB number not yet generated because the FB has never been called in the program Place at least one call of the FB in OB1 or a wrapper, then recompile
Compiler error: "Access to instance data not permitted" FB has the Multiple Instance Capability = No attribute, used as multi-instance Use the FB as a single instance with its own DB
Runtime: value reads zero or stale Absolute access offset wrong after FB declaration change Switch to symbolic or AT-view access
Compiler warning: "Symbol 'Motor1_DB' is not unique" Two blocks with the same name in different program folders, or DB manually renamed twice Rename via PLC tag table; compile project fully
SFC/Graph FB cannot read drive status Step enables a transition that needs a flag buried in drive instance DB Expose the flag as a VAR_OUTPUT on a wrapper FB instead of direct DB access
Webserver or OPC UA cannot see the tag Tag not enabled in the data block properties for HMI/OPC visibility Open DB properties → Attributes → enable 'HMI visible' / 'OPC UA accessible'
CEMAT library update breaks offset-based access Library author added/removed statics, shifting all offsets Migrate to symbolic or parameter-pass interface; never use absolute offsets across library versions

Verification

  1. Compile the project (Build > Compile all). TIA Portal reports errors per block.
  2. Download the program to the PLC (Online > Download to device).
  3. Go Online, open the calling FB, and click Monitor/Modify. Confirm the read/write tags inside the multi-instance block update when you change them from the caller.
  4. Open the target instance DB in Data view and confirm offsets match the static declaration order in the FB.
  5. From the HMI or Watch table, force the symbolic tag "Motor_1_DB".SpeedRef to a known value and verify the connected drive responds.
  6. Stop the CPU, cold-start, and verify the instance DB retains its non-volatile statics (default for standard instance DBs; do not change the Non-Retain attribute unless volatility is required).

Best Practices

  • Prefer symbolic or parameter-passed access; treat absolute DB offsets as diagnostic-only.
  • Use multi-instance whenever you consume a library FB from your own wrapper, to avoid DB explosion.
  • When you must read a private static of a library FB, declare an AT view on a matching UDT — never guess the offset.
  • Enable HMI/OPC visibility on the parent wrapper's DB, not on the library instance DB, when possible.
  • Document in the wrapper FB's header which underlying instance DB is being wrapped so that commissioning engineers can find it.

FAQ

Can I call an instance DB of an FB in SCL using only the FB block name (no symbol name)?

Yes. The TIA Portal compiler creates a default symbol "<FBName>_DB" the moment the FB is called in the program. You can reference this auto-symbol from any SCL source in the same PLC without manually editing the symbol table, e.g. "MotorUni_DB".SpeedRef := 1200.0;.

Is there a way to avoid the auto-generated instance DB altogether?

Yes — use a multi-instance. Declare the FB as a static variable inside a wrapper FB, and the compiler stores its statics in the wrapper's instance DB. No separate DB is generated and no symbol entry is needed in the global symbol table.

What is the difference between symbolic and absolute access to an instance DB?

Symbolic access ("DB_Symbol".Variable) is checked at compile time and refactor-safe; the compiler updates the offset when the FB changes. Absolute access (DB20.DBD12) uses a raw byte offset that is NOT checked and breaks silently when the FB layout changes. Use absolute access only for diagnostics.

Why does the compiler reject my multi-instance call of a CEMAT block?

Most Siemens library FBs, including CEMAT V8+ motor blocks, are multi-instance capable. If the compiler reports "Instance data not permitted", the FB was published with the attribute disabled — in that case, call it as a single instance and let the compiler create the dedicated DB.

Can I read instance DB tags from S7-1500 OPC UA or Webserver using the default symbol?

Yes, but you must enable the Accessible from HMI/OPC UA/Webserver attribute on the instance DB. TIA Portal hides DB contents from OPC UA by default for performance. The simplest pattern is to expose the value as a VAR_OUTPUT on a wrapper FB and enable visibility on the wrapper's DB instead.

Back to blog