Generating Standalone DBs in SCL for S7-300/400 and TIA Portal

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

Generating Standalone Data Blocks in SCL for S7-300/400 and TIA Portal

Structured Control Language (SCL) on Siemens SIMATIC S7-300/400 controllers (STEP 7 V5.x) and the TIA Portal engineering environment allows engineers to declare Data Blocks (DBs) directly in source files rather than assembling them field-by-field in the LAD/FBD/STL editor. This is the recommended approach for projects that need long configuration tables, parameter lists, recipe structures, or shared memory maps referenced by multiple Function Blocks (FBs) and Functions (FCs).

This article documents the canonical SCL syntax for declaring a global, standalone DB, contrasts it with instance-DB generation, and shows how to leverage the TIA Portal Openness API to automate bulk generation. The patterns apply to SIMATIC S7-300, S7-400, S7-1200, and S7-1500 controllers, with notes on syntax differences between STEP 7 V5.x and TIA Portal.

1. Prerequisites

  • STEP 7 V5.5 / V5.6 (or compatible) with the S7-SCL add-on installed, OR TIA Portal V16 / V17 / V18 / V19 / V20 / V21 with the SCL editor.
  • For automated bulk generation: TIA Portal Openness API installed (licensed add-on on TIA Portal).
  • Source organization: an SCL source file (.scl) inside the S7 program / PLC software tree, OR a SCL/STL source container in the Sources folder of the S7 project.
  • Symbol table access: the global DB symbol must be declared as DB n where n is the DB number you reserve.
Note: STEP 7 V5.x SCL was packaged as "S7-SCL" and is documented in the manual S7-SCL for S7-300/400 - Manual (entries starting around section 5 describing DATA_BLOCK grammar). TIA Portal ships the same grammar for the S7-1200/1500 controllers.

2. Understanding DB Types: Instance vs Global

Before declaring a DB, decide which kind you actually need.

DB Type Created by Tied To Typical Use Auto-Generated?
Instance DB FB call / instance declaration A specific FB Holds static VAR of that FB across calls Yes, when you instantiate the FB
Global / Standalone DB Manual creation, SCL source, or Openness API Nothing - freely referenced Shared configuration, recipes, lookup tables No, must be declared explicitly
System DB (SDB) STEP 7 internally Hardware / CPU config Hardware interrupt assignments, etc. Generated by HW Config / device config
Array DB Manual creation None Large contiguous typed memory No

The user's pattern - one configuration DB referenced from many FBs - is a global, standalone DB. It must NOT be created as an instance of any FB, otherwise SCL will append the FB's VAR_INPUT, VAR_OUTPUT, VAR_IN_OUT, VAR_STATIC, and VAR_TEMP regions into it, which is exactly the behavior the user reported as undesirable.

3. Declaring a Standalone Global DB in SCL (STEP 7 V5.x / TIA Portal)

The grammar for declaring a global DB in an SCL source file is:

DATA_BLOCK <DB name or number>
{ TITLE = '<comment text>' ; }
{ AUTHOR : '<author>' ; }
{ FAMILY : '<family>' ; }
{ NAME : '<name>' ; }
{ VERSION : <number> ; }
STRUCT
  <variable declaration>
  ...
END_STRUCT ;
BEGIN
  <initialization assignments>
END_DATA_BLOCK

The minimum required form is:

DATA_BLOCK logic_config
STRUCT
  input_list    : ARRAY[0..8191] OF WORD;
  block_list    : ARRAY[0..8191] OF STRUCT
                    typ        : BYTE;
                    inputs     : WORD;
                    input_index: WORD;
                  END_STRUCT;
END_STRUCT ;
BEGIN
END_DATA_BLOCK

Key points to observe:

  1. The block is opened with the keyword DATA_BLOCK and closed with END_DATA_BLOCK. There is no trailing semicolon after END_DATA_BLOCK.
  2. The block name (logic_config) must be unique within the S7 program. It is registered in the symbol table as type DB with the address assigned by the compiler.
  3. The header attributes (TITLE, AUTHOR, FAMILY, NAME, VERSION) are optional but TITLE is the comment that appears in the block properties dialog and the cross-reference.
  4. The STRUCT wrapper around the variable declarations is mandatory in SCL for a global DB; it ensures the compiler allocates the DB with a length matching the sum of the members.
  5. The BEGIN ... END_DATA_BLOCK section holds initialization values and is optional. If you need default values, list them by symbolic name. If you omit the section, the DB is filled with zeros (or the configured retentive defaults from the CPU).

4. Referencing the Standalone DB from FBs and FCs

Once compiled, the DB is treated as a global symbol. You can access its members from any code block (OB, FB, FC) using fully qualified symbolic names:

my_index := logic_config.block_list[logic_pos].input_index;
my_input := logic_config.input_list[my_index];

Or by absolute addressing using the DB number assigned by the compiler:

      L     DBW [AR1,P#0.0]      // load logic_config.input_list[my_index]
      T     MW 100

For S7-300/400, a global DB occupies a single open DB register. You open it with OPN DB (STL) or implicitly by using fully qualified symbolic access in SCL.

Retentivity: Mark the relevant tags of the global DB as retentive in the block properties (or in the declaration with the RETAIN attribute). In STEP 7 V5.x, set the Non-Retain area in the DB properties. In TIA Portal, check the "Retain" column in the tag table of the DB. Tags outside the retentive area are reset to their initial values on a cold restart; tags inside are preserved across power cycles and warm restarts.

5. Step-by-Step Workflow: Creating the DB Inside the SCL Editor

The fastest, most error-resistant way to write a long DB in SCL is to start from the SCL editor template rather than typing the DATA_BLOCK header by hand.

5.1 STEP 7 V5.x / S7-SCL

  1. Open the S7 project in SIMATIC Manager.
  2. Right-click the Sources folder of the S7 program and choose Insert New Object → SCL Source.
  3. Open the source file by double-clicking it. The SCL editor launches.
  4. From the menu, choose Insert → Block Template → DB. A pre-populated DATA_BLOCK skeleton is inserted at the cursor.
  5. Fill in the variable declarations between STRUCT and END_STRUCT.
  6. Choose File → Compile (or press Ctrl+F7) to generate the DB.
  7. Open the generated DB in the LAD/FBD/STL editor to verify length, offsets, and default values.

5.2 TIA Portal (S7-1200 / S7-1500 / S7-300/400 as configured)

  1. In the project tree, right-click the Program blocks folder of the PLC device.
  2. Choose Add new block → Data block.
  3. Set the type to Global DB (default), assign a number, and confirm.
  4. The DB editor opens in the TIA Portal with a table of declared tags. You can paste a tab-separated list directly into the table for bulk creation.
  5. Alternatively, use External source files: add a .scl source to the External Sources folder, right-click, and choose Generate blocks from source.

Both paths end in a single global DB whose members can be referenced from any code block in the program.

6. Auto-Generation Patterns: ARRAY, STRUCT, UDT

Long configuration lists are best expressed as ARRAY elements with STRUCT element types or as a User-Defined Type (UDT) repeated by an ARRAY. UDTs are particularly useful when the same shape is needed in several DBs.

6.1 Inline ARRAY of STRUCT (no UDT)

DATA_BLOCK motor_table
STRUCT
  motor : ARRAY[1..64] OF STRUCT
           name        : STRING[16];
           rated_rpm   : REAL;
           rated_kw    : REAL;
           enable_tag  : BOOL;
           fb_instance : INT;   // index of associated FB instance DB
         END_STRUCT;
END_STRUCT ;
BEGIN
END_DATA_BLOCK

6.2 UDT-Based Repetition

First declare a UDT (this example is TIA Portal SCL syntax; STEP 7 V5.x uses TYPE ... END_TYPE with a separate UDT block):

TYPE "UDT_motor"
STRUCT
  name        : STRING[16];
  rated_rpm   : REAL;
  rated_kw    : REAL;
  enable_tag  : BOOL;
  fb_instance : INT;
END_STRUCT;
END_TYPE

Then declare the DB using the UDT:

DATA_BLOCK motor_table
STRUCT
  motor : ARRAY[1..64] OF "UDT_motor";
END_STRUCT ;
BEGIN
END_DATA_BLOCK

Advantages of the UDT approach:

  • Single source of truth for the record shape - one change updates every DB that uses it.
  • Bulk initialization in the BEGIN block is cleaner: motor[1].rated_rpm := 1450.0;
  • Easier to export/import through TIA Portal Openness (see Section 7).

6.3 STEP 7 V5.x SCL UDT syntax

In STEP 7 V5.x, UDTs are block objects (block type UDT) created in the S7 program. The SCL source to declare one is:

TYPE UDT10
STRUCT
  name      : STRING[16] ;
  rated_rpm : REAL ;
  rated_kw  : REAL ;
  enable_tag: BOOL ;
  fb_instance : INT ;
END_STRUCT ;
END_TYPE

And the DB referencing the UDT:

DATA_BLOCK DB100
STRUCT
  motor : ARRAY[1..64] OF UDT10 ;
END_STRUCT ;
BEGIN
END_DATA_BLOCK

7. TIA Portal Openness API for Bulk DB Generation

When a project requires hundreds or thousands of tags in a global DB - for example a configuration table generated from a spreadsheet - manually editing the DB is impractical. TIA Portal exposes the Openness API, a .NET API that drives the engineering UI programmatically.

Note: The Openness API is licensed separately and is part of the TIA Portal installation. It supports C#, VB.NET, and any CLR-compatible language. Reference: TIA Portal Openness API - Automation of Engineering Workflows.

7.1 High-Level Workflow

  1. Add a reference to Siemens.Engineering and Siemens.Engineering.Hmi in your .NET project.
  2. Open a TIA Portal instance via new TiaPortalInstance(TiaPortalMode.WithUserInterface) or WithoutUserInterface.
  3. Open the project with project.Open(...).
  4. Navigate to the target PLC's Program blocks folder.
  5. For each DB to create: instantiate a PlcBlockUserGroup or directly add a PlcBlock with BlockType = DataBlock.
  6. Populate the PlcBlock's Interface by writing the SCL source body to a PlcTextAssociation or by setting the Attributes collection.
  7. Call plcBlock.Compile() to compile the DB inside TIA Portal.

7.2 C# Skeleton (Conceptual)

using Siemens.Engineering;
using Siemens.Engineering.SW;
using Siemens.Engineering.SW.Blocks;

var tia = new TiaPortalInstance(TiaPortalMode.WithUserInterface);
var project = tia.GetOpenProject() ?? tia.Projects.Open(new FileInfo(@"C:\Proj\MyProj.ap21"));
var device = project.GetService<DeviceService>().Devices.First();
var sw = device.GetService<SoftwareContainer>().Software as PlcSoftware;
var blockGroup = sw.BlockGroup.Groups.Find("Program blocks").Groups.Find("MyConfig");

PlcBlock db = PlcBlock.Create(blockGroup, "logic_config", BlockType.DataBlock, 0 /* auto-number */, true /* isDB */, null);
db.ProgrammingLanguage = ProgrammingLanguage.SCL;
db.Interface.TextualSclSource = @"
DATA_BLOCK logic_config
STRUCT
  input_list : ARRAY[0..8191] OF WORD;
  block_list : ARRAY[0..8191] OF STRUCT
    typ         : BYTE;
    inputs      : WORD;
    input_index : WORD;
  END_STRUCT;
END_STRUCT;
BEGIN
END_DATA_BLOCK
";
db.Compile();

8. XML Schema Notes for SCL DB Export/Import

When DBs are exported from TIA Portal (for example via the project archive or a custom Openness script), the data is serialized to an XML form. Two non-obvious rules govern round-tripping SCL DBs through XML.

  1. The % prefix on the DB name is stripped in XML. The % character is added automatically by the TIA Portal importer when the block is reconstructed. The XML payload therefore contains the bare name, e.g. <Name>logic_config</Name> even though the in-project name is %logic_config. Reference: Export/Import of Structured Types of SCL Blocks - TIA Portal.
  2. Whitespace between address parts is allowed. Symbolic addresses such as DB10.DBX0.0 can appear in the XML as DB10 . DBX 0 . 0. Both forms import identically. The importer normalizes the spacing.

These rules matter when you build a generator that produces XML to feed back into TIA Portal: do not inject literal % characters, and do not worry about tightening or relaxing internal whitespace in address strings.

9. Verification

After generating the DB, perform the following checks before downloading to the CPU.

  1. Compile cleanly. In STEP 7 V5.x, the status bar shows 0 errors, 0 warnings. In TIA Portal, open the Info → Compile tab and confirm the same.
  2. Check DB length. In the block properties, the "Length in bytes" must equal the sum of the member sizes. For the example, 8192 * 2 = 16384 bytes for input_list plus 8192 * 5 = 40960 bytes for block_list, total 57344 bytes (within the S7-300/400 DB size limits for the CPU in use).
  3. Cross-reference. Use Options → Cross References in STEP 7 V5.x or the cross-reference in TIA Portal to confirm that the FBs and FCs that should read the configuration DB do so, and that no instance DB is accidentally pointing at the same number.
  4. Watch table test. Open a watch table, force a value in the new DB, run a single scan, and verify the consumer FB sees the change. For an S7-300/400, use the VAT or the Monitor/Modify function with a breakpoint on the consuming FB.
  5. Retain test. With the configuration DB marked retentive, perform a power cycle (off / on) and confirm values are preserved.

10. Common Errors and Troubleshooting

Symptom Probable Cause Fix
Compiler "Identifier already used" The DB name collides with an existing symbol or an instance DB number Rename the DB or change the reserved DB number in the symbol table
Compiler "Type not declared" for a STRUCT member Inline STRUCT missing END_STRUCT or wrong nesting Recheck brackets; SCL requires explicit closure of every STRUCT and ARRAY
DB has extra members inherited from an FB The block was compiled as an instance of an FB Re-declare as a global DB using the DATA_BLOCK template in SCL
Values reset to zero on warm restart Tags are not marked retentive Open the DB properties, set the retentive boundary past the last needed tag
Openness import creates a DB named %logic_config Source XML included a literal % prefix Strip the % from the XML; the importer will add it automatically
DB length exceeds work memory ARRAY bounds too large for the target CPU Reduce the upper bound, split into multiple DBs, or migrate to a CPU with a larger work memory
Compiler "STRING without length specification" Use of STRING without bracketed length Always specify STRING[n] with explicit length

11. S7-300/400 vs S7-1200/1500 Differences

The DATA_BLOCK syntax is essentially identical between STEP 7 V5.x and TIA Portal, with these practical differences:

  • UDT declaration site. STEP 7 V5.x uses a standalone UDT block (UDT n). TIA Portal uses the TYPE ... END_TYPE region inside a global DB or inside a PLC data type block.
  • Optimized vs standard block access. S7-1500 DBs are optimized by default; symbolic but not absolute access is allowed. S7-300/400 DBs are standard by default; both are allowed.
  • Retentive declaration. TIA Portal lets you tick "Set in IDB" per tag. STEP 7 V5.x uses a single retentive boundary in the DB properties.
  • Initialization syntax. Identical in both worlds; multi-line initializers in the BEGIN block are compiled identically.

12. Field-Proven Tips

  • Start from the template, not the empty file. The Insert → Block Template → DB menu item in the SCL editor pre-fills the header correctly and is faster than hand-typing DATA_BLOCK ... END_DATA_BLOCK.
  • Use ARRAY of UDT for tables. Editing 64 individual STRUCT definitions is error-prone; one UDT and an ARRAY of it is 5 lines of code.
  • Reserve the DB number. In STEP 7 V5.x, place a placeholder symbol in the symbol table with the desired DB number before compiling, so the compiler does not assign an unexpected number that collides with an instance DB.
  • Mind the CPU limits. S7-300 CPUs (CPU 312/314/315/317/319) impose per-DB size limits (typically 8 KB to 64 KB depending on the CPU). The example with 57344 bytes fits a CPU 319 but not a CPU 312. Check the CPU datasheet before declaring large ARRAYS.
  • Avoid instance DBs for shared data. A common antipattern is to store shared configuration inside an FB's instance DB and read it from other FBs via "multi-instance" tricks. This creates hidden coupling and makes the project harder to maintain. Use a global DB instead.
  • Compile the SCL source as a single unit. When the SCL source file contains the DATA_BLOCK declaration, the SCL compiler resolves forward references and emits the DB in the right order. If you mix UDT, FB, and DB declarations in one source file, compile the whole file at once.

FAQ

Why does the SCL compiler add FB local variables to my DB?

Because the DB has been created as an instance of an FB rather than a global DB. Re-declare the block using DATA_BLOCK <name> as the first line (the standalone header, not the instance header that SCL inserts when an FB is placed). The Insert → Block Template → DB menu item always inserts the standalone header.

How large can a single global DB be on an S7-300/400?

It depends on the CPU. A CPU 312 limits DBs to 8 KB; CPU 315-2 DP to 16 KB; CPU 317-2 to 64 KB; CPU 319-3 to 64 KB. The S7-400 series extends this to 64 KB on most CPUs and several MB on the CPU 417. Always check the CPU datasheet for the exact figure.

Can I declare a global DB inside the same SCL source as FBs?

Yes. The SCL source file may contain any combination of TYPE, DATA_BLOCK, FUNCTION_BLOCK, and FUNCTION declarations, in any order. Compile the entire source file in one pass so the compiler can resolve cross-references.

How do I keep my configuration DB values across a power cycle?

Mark the tags that must survive as retentive. In STEP 7 V5.x, set the retentive boundary in the DB properties past the last retained tag. In TIA Portal, tick the "Retain" column in the DB tag table for each tag you want to keep. The CPU preserves retained values across power off / on and across warm restarts.

What is the difference between a UDT and a DB with an inline STRUCT?

A UDT is a reusable type definition. You declare it once and reference it from many DBs, FBs, or FCs as a custom data type. An inline STRUCT is a one-off type used only inside the DB where it appears. Use UDTs when the same record shape is needed in more than one place or when you want to change the shape in one location and have it propagate everywhere.

Back to blog