Resolving TIA Portal Know-How Protection Global Access Errors

David Krause14 min read
SiemensTIA PortalTroubleshooting
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

Resolving TIA Portal Know-How Protection Global Access Errors

Siemens' Know-How Protection (KHP) for SIMATIC S7-1200 and S7-1500 blocks is a compile-time obfuscation feature, not a runtime encryption. KHP is valid only when the protected FC/FB/OB is self-contained: the compiler must be able to resolve every symbol referenced inside the block at compile time without traversing the global symbol table, global DBs, or external FB instances. Any unresolved cross-block dependency triggers the compile error The know-how protected block contains invalid access to global PLC constants (or the equivalent message for instances, tags, and global data blocks). The protection will be stripped automatically and the block reverts to plain SCL/stub source on next download if the dependency is not removed.

This article covers the root cause, the Siemens-documented constraints, and four engineering patterns that eliminate the violation while still delivering a re-distributable protected library.

1. Problem Overview

Typical symptom when compiling or downloading a project in TIA Portal V17/V18/V19 that contains KHP blocks:

Compile error (0500:0073):
The know-how protected block "FB_LineControl [FB100]"
contains invalid access to global PLC constants.

Download aborted: 1 error(s), 0 warning(s).
Affected blocks: FB100, FB101, FB102, FC55

Variants of the same compiler message you will encounter:

Trigger Compiler message Block state
Direct read of global constant Invalid access to global PLC constants KHP stripped, source re-exposed
Direct read of global tag/Merker Invalid access to global PLC tags KHP stripped
Call to a non-KHP external FB Invalid access to non-protected block instance KHP stripped, instance visible
Read of a global DB element Invalid access to global data block KHP stripped
Indirect/fully-qualified DB access Invalid access to address area KHP stripped
Critical: TIA Portal does not warn at the moment you click Know-How Protection on a block. The error appears at the next full project compile or download. If the customer has a routine that re-downloads from a project, KHP silently reverts to plain source in their archive.

2. Root Cause Analysis

Know-How Protection works by replacing the protected block's compiled code with a black-box section in the offline program and a stripped source on the online card. For the compiler to perform this substitution, every symbol the block references at runtime must already be resolved to a fixed address or be carried inside the block itself. The rules TIA Portal enforces per the official SIMATIC S7-1500 manual collection are summarized as follows:

  • The block may only access local TEMP, STAT, and constant CONST symbols declared in its own interface.
  • The block may only read/write INPUT, OUTPUT, IN_OUT, and STAT parameters it received from its caller.
  • References to global PLC tags, global constants (symbolic or absolute), Merker/Flag bytes, Timers, Counters, and standard global DBs are forbidden inside the protected section.
  • Calls to other FBs are allowed only if the called FB is itself a multi-instance of a KHP block, or if the call is performed on a local STAT instance declared inside the KHP block.
  • System functions (SFB/SFC) and system blocks that are part of the firmware (e.g., RD_SYS_T, TON, TP) are permitted but their parameter binding is restricted to local literals and parameters.

According to the Siemens support entry "What are the requirements to be met by know-how-protected blocks so that you can compile them also in other projects?", KHP blocks must be compiled separately for each CPU series. A KHP block written for S7-1500 will not load onto an S7-1200 even if the CPU accepts the same firmware family, and vice versa. The TIA Portal reference for KHP on the S7-1500 ET 200MP manual collection is at SIMATIC S7-1500 ET 200MP Manual Collection - Know-How Protection.

3. Siemens KHP Constraints and Compilation Rules

Review these requirements before you commit to a KHP architecture. They are the same constraints that the TIA Portal compiler silently enforces during the build.

Constraint S7-1200 S7-1500 Comment
KHP on FC Supported Supported FC has no instance DB; STAT and instance-of-self not applicable
KHP on FB Supported Supported Multi-instance is allowed; single-instance must be declared STAT
KHP on OB Limited Supported OB1 cyclic calls behave like any other block
KHP on global DB Not supported Supported Use Block Privacy attribute on global DBs as alternative
Direct read of Merker/Input/Output process image Forbidden Forbidden Route through IN/OUT parameters
Cross-series compilation No No Build one KHP library per CPU series
Maximum KHP blocks per library Limited by project Limited by project Compiler iterates per-block; large libraries take longer
Library type required for distribution Master/global copy Master/global copy Type-based update requires master copy in a library
Memory layout note: On S7-1200/S7-1500, IN_OUT parameters of complex types (UDT, ARRAY, STRUCT) are passed by reference (pointer to the caller's memory). Inside the KHP block, the address of the caller's UDT is exposed symbolically. This is permitted by KHP because the binding is local to the call site; the symbol the KHP block sees is its own parameter, not a global tag.

4. Architectural Pattern: Self-Contained KHP Blocks

The single rule that makes KHP work is: every name the KHP block touches must belong to that block. Practical patterns to achieve this:

  1. Hoist globals into the interface. Move global constants, configuration values, and machine parameters into INPUT or IN_OUT parameters. The caller passes the value in.
  2. Localize DBs. Replace global DB reads with a single IN_OUT parameter typed as a UDT. The caller passes a DB element of that UDT type.
  3. Move multi-instance FBs into the KHP block's STAT. Instead of calling "Recipe".FB_MotorCtrl(...) on a globally declared FB, declare i_MotorCtrl : FB_MotorCtrl in the STAT area of the KHP FB and call i_MotorCtrl(...).
  4. Split the block. Where the dependency cannot be moved in, split the logic into a pre-call KHP block and a post-call KHP block, with a thin non-KHP coordinator in the middle that performs the external call.

The four engineering solutions that follow implement these patterns in concrete SCL.

5. Solution 1: Pass Globals as In/Out Parameters

Replace every direct read of a global constant inside the KHP block with an INPUT parameter. The caller (typically OB1 or a non-protected coordinator FB) reads the global and passes the value in.

Before — violation:

// FB_LineControl [FB100] - KHP enabled - WILL NOT COMPILE
FUNCTION_BLOCK "FB_LineControl"
VAR
    i_State : INT;
END_VAR
BEGIN
    // Direct global constant read - rejected by KHP
    IF i_State > "Glob_Const".MaxSpeed THEN
        "Glob_Const".Fault := TRUE;
    END_IF;
END_FUNCTION_BLOCK

After — KHP clean:

// FB_LineControl [FB100] - KHP enabled - compiles and protects
FUNCTION_BLOCK "FB_LineControl"
VAR_INPUT
    i_MaxSpeed   : REAL;          // was "Glob_Const".MaxSpeed
    i_TargetRPM  : REAL;
END_VAR
VAR_OUTPUT
    o_Fault      : BOOL;          // returned to caller for write-back
    o_OverSpeed  : BOOL;
END_VAR
VAR
    i_State : INT;
END_VAR
BEGIN
    IF i_State > i_MaxSpeed THEN
        o_Fault := TRUE;
        o_OverSpeed := TRUE;
    END_IF;
END_FUNCTION_BLOCK

Caller side, in OB1 or a non-protected coordinator:

// Read globals outside KHP, pass values in
"FB_LineControl_DB"(i_MaxSpeed := "Glob_Const".MaxSpeed,
                    i_TargetRPM := "Recipe".TargetRPM,
                    o_Fault     => "Glob_Const".Fault);

Because the KHP block only references its own parameters, the compiler resolves all symbols locally and KHP is preserved.

6. Solution 2: Inline Constants into KHP Blocks

When the global value never changes at runtime (e.g., mechanical limits, scaling factors), copy the literal into the KHP block. Add an // source: Glob_Const.MaxSpeed rev. 4 comment for traceability.

FUNCTION_BLOCK "FB_LineControl"
VAR CONST
    // Source: Glob_Const.MaxSpeed - rev 4 - mechanical limit
    C_MAX_SPEED : REAL := 3500.0;
    // Source: Glob_Const.MinSpeed - rev 4
    C_MIN_SPEED : REAL := 250.0;
END_VAR
BEGIN
    IF i_State > C_MAX_SPEED THEN
        o_Fault := TRUE;
    END_IF;
END_FUNCTION_BLOCK

VAR CONST symbols are owned by the block, so KHP accepts them. Maintain a single master spreadsheet (or a non-protected source library) for the original constants so updates stay synchronized. The drawback is that the KHP block is no longer driven from a single tuning point; you must recompile and re-distribute the KHP library when the constant changes.

7. Solution 3: UDT-Based Obfuscation with Endian Swapping

For complex state, configuration, and tuning surfaces, define a UDT that the KHP block accepts as IN_OUT. The caller passes a DB element of that UDT type. The UDT contract is opaque to anyone reading the customer project: they see only a typed reference, not what each field means.

// UDT_ConfTuning - declared once in the master library
TYPE "UDT_ConfTuning"
    STRUCT
        n_PGain       : REAL;   // mapped from "Glob_Conf".PGain
        n_IGain       : REAL;   // mapped from "Glob_Conf".IGain
        n_DGain       : REAL;   // mapped from "Glob_Conf".DGain
        n_StrokeLimit : DINT;
        n_RampTime    : TIME;
        n_Reserved    : ARRAY[0..7] OF BYTE;
    END_STRUCT;
END_TYPE

KHP block:

FUNCTION_BLOCK "FB_AdvControl"
VAR_IN_OUT
    io_Tuning : "UDT_ConfTuning";     // by reference - zero copy on S7-1500
END_VAR
VAR
    s_Tune : "UDT_ConfTuning";        // local working copy
END_VAR
BEGIN
    // Copy to local, scrub global linkage
    s_Tune := io_Tuning;
    // ...proprietary math using s_Tune.n_PGain, etc...
    io_Tune.n_StrokeLimit := s_Tune.n_StrokeLimit + 1;
END_FUNCTION_BLOCK

To increase reverse-engineering cost on disassembled block content, byte-swap multi-byte values at the boundary. TIA Portal does not expose a BSWAP instruction directly; use WORD_TO_INT patterns or rotate through DWORD registers:

// Obfuscation helper: swap bytes of a DWORD on the boundary
FUNCTION "FC_BSwap32" : DWORD
VAR_INPUT
    i_In : DWORD;
END_VAR
VAR
    b0 : BYTE;
    b1 : BYTE;
    b2 : BYTE;
    b3 : BYTE;
    t  : DWORD;
END_VAR
BEGIN
    b0 := DWORD_TO_BYTE(SHR(IN:=i_In, N:=24) AND 16#FF);
    b1 := DWORD_TO_BYTE(SHR(IN:=i_In, N:=16) AND 16#FF);
    b2 := DWORD_TO_BYTE(SHR(IN:=i_In, N:=8)  AND 16#FF);
    b3 := DWORD_TO_BYTE(i_In AND 16#FF);
    t  := SHL(IN:=BYTE_TO_DWORD(b0), N:=0)
       OR SHL(IN:=BYTE_TO_DWORD(b1), N:=8)
       OR SHL(IN:=BYTE_TO_DWORD(b2), N:=16)
       OR SHL(IN:=BYTE_TO_DWORD(b3), N:=24);
    FC_BSwap32 := t;
END_FUNCTION

The caller converts to swapped form on entry, the KHP block works on native little-endian internally, and the caller swaps back on exit. From the customer's perspective, the values in the DB element do not match the engineering meaning of the field name without the swap helper, which is also delivered as KHP.

8. Solution 4: Split-Block Pattern for External Calls

When the KHP block must call a non-KHP external FB or function (e.g., a standard recipe block, a vendor-supplied FB), split the algorithm into two KHP blocks. The unprotectable coordination runs in the middle and is delivered as plain source, but it contains no proprietary logic — only the call wiring.

// Pre-call KHP block - all proprietary preprocessing
FUNCTION_BLOCK "FB_PreProcess"
VAR_IN_OUT
    io_Data : "UDT_RecipeData";
END_VAR
BEGIN
    // proprietary normalization
END_FUNCTION_BLOCK

// External non-KHP coordinator - the unprotected glue
FUNCTION "FC_RecipeCoordinator" : VOID
VAR_IN_OUT
    io_Data : "UDT_RecipeData";
END_VAR
BEGIN
    "FB_PreProcess_DB"(io_Data := io_Data);
    "FB_VendorRecipe"(io_Data := io_Data);  // vendor FB, not KHP
    "FB_PostProcess_DB"(io_Data := io_Data);
END_FUNCTION

// Post-call KHP block - all proprietary postprocessing
FUNCTION_BLOCK "FB_PostProcess"
VAR_IN_OUT
    io_Data : "UDT_RecipeData";
END_VAR
BEGIN
    // proprietary closure / validation
END_FUNCTION_BLOCK

Information leakage is limited to the call order and the UDT shape. The UDT itself can be obfuscated using the byte-swap helper from section 7.

9. Handling FB Instance Calls Inside KHP Blocks

If the KHP FB must call another FB, declare the called FB as a STAT multi-instance. Multi-instances share the instance DB of the KHP block, so the compiler does not need to resolve a global instance name.

FUNCTION_BLOCK "FB_LineControl"
VAR
    i_MotorCtrl : "FB_MotorCtrl";  // multi-instance, lives inside FB_LineControl's IDB
    i_Valve     : "FB_ValveCtrl";  // multi-instance
END_VAR
BEGIN
    i_MotorCtrl(i_Speed := i_TargetRPM,
                o_Ready => o_MotorReady);
    i_Valve(b_Open := b_ValveCmd,
            b_Ack  => o_ValveAck);
END_FUNCTION_BLOCK

Multi-instance FBs must be KHP themselves if you do not want the inner logic visible. Both FBs are then placed in the same master-copy library and distributed together.

Gotcha: If a non-KHP FB is called inside the KHP block as a multi-instance, the KHP block's protection is still valid; the unprotected FB's code remains visible to the customer on download. Do not put proprietary algorithms in any FB that is not itself KHP.

10. S7-1200 vs S7-1500 Implementation Notes

Aspect S7-1200 S7-1500
UDT in IN_OUT Passed by reference (optimized block) Passed by reference (optimized block)
KHP on standard DB Not supported — use privacy on instance DB Supported on global DBs
Maximum UDT size in IN_OUT Limited by work memory Larger practical limit; depends on firmware
Optimized block access Default for new blocks Default for new blocks
Multi-instance FBs Supported Supported with broader feature set (e.g., parameter instance)
Cross-series KHP compile Not allowed Not allowed
TIA Portal version minimum V13 SP1+ for KHP basics V13 SP1+; V18+ for current KHP behavior

Build one master-copy library per CPU series. According to the Siemens KB entry cited above, the same source code can be compiled into two master copies: one for S7-1200 firmware targets and one for S7-1500 firmware targets, and both can carry KHP simultaneously.

11. Compilation, Distribution, and Cross-Project Use

The lifecycle that keeps KHP intact when the customer has download rights:

  1. Build the master-copy library. Place every KHP block in a single TIA Portal global library as a master copy. Enable Know-How Protection on each block before saving the master.
  2. Type-based distribution. Add the master copy to the customer's project as a Type, not as a single instance. Type-based reuse updates all instances in the customer's project when you update the master.
  3. Versioned updates. Always increment the library version (right-click library → Properties → Version) when KHP blocks change. The customer's TIA Portal will warn about version mismatch and refuse an uncontrolled overwrite.
  4. Compile to the customer's CPU series. Confirm the firmware version on the customer's PLC (e.g., S7-1515-2 PN, FW V2.9). Recompile the master against the same firmware family, or at minimum the same CPU series, before delivering.
  5. Block consistency check. In the customer project, run Project → Compile → Software (rebuild all blocks). Any KHP block that lost protection during a manual edit in the customer project will fail this rebuild with the same invalid access message.
  6. Anti-tamper verification. Use the read-only password on the CPU (Properties → Protection → CPU access protection) in addition to KHP. KHP does not prevent online block read-back; it only obscures the offline source.

Compared with the SIMATIC TDC compiled library workflow (where libraries are distributed as pre-compiled binaries), TIA Portal still ships source-derived KHP blocks. The closest equivalent is to set Library → Read-only on the master copy and distribute it as a .tias library file with KHP applied to every block.

12. Verification Checklist

Run these checks after any refactor of a KHP block. All items must pass before delivery.

# Check Expected result
1 Project compile (rebuild all) 0 errors, 0 warnings about access
2 Block properties → Protection "Know-how protection" still checked
3 Online → Accessible nodes → block read-back Source shown as "<protected>", interface visible
4 CPU download simulation No error 0500:0073 in download log
5 Library type compatibility report All instances updated to new master version
6 Cross-series compile test (S7-1200 master + S7-1500 master) Both masters compile, both load on their target CPU
7 Reverse engineering attempt: open KHP block in offline editor No source visible; only interface and code-stub marker
8 Indirect access scan: any " operator referencing a global name inside the KHP block None found
Field tip: Use Project tree → Find and replace with the regex "[A-Z][A-Za-z0-9_]+" to surface any quoted global-symbol access inside protected blocks before the compile does it for you.

13. FAQ

Why does TIA Portal strip Know-How Protection on download even though I enabled it on the FB?

The block references a global symbol (PLC tag, global constant, or external FB instance) that the KHP compiler cannot resolve locally. TIA Portal silently removes KHP and ships plain source. Move the global into an IN/OUT parameter or declare a multi-instance in the FB's STAT area to restore protection.

Can a single KHP block run on both S7-1200 and S7-1500 from one library file?

No. Per the Siemens KB entry on cross-project KHP compilation, the master copy must be compiled separately for each CPU series. Build two master copies from the same source — one for S7-1200, one for S7-1500 — and ship both in the global library.

Does Know-How Protection prevent online read-back of the block from the PLC?

No. KHP only obfuscates the offline source in the project. A connected engineer with the appropriate CPU access password can still read the compiled code from the PLC. Combine KHP with a CPU read-only password and access level 2/3/4 protection for actual runtime confidentiality.

Is there a TIA Portal equivalent of SIMATIC TDC compiled libraries?

No compiled-binary distribution exists for S7-1200/S7-1500. The closest equivalent is a TIA Portal global library containing only master copies with KHP applied, distributed as a .tias file with the master marked read-only.

What is the maximum size of a UDT passed as IN_OUT to a KHP block?

It is bounded by the work memory of the target CPU. The S7-1500 ET 200MP manual collection notes that large UDTs in IN_OUT are passed by reference, so the memory hit is a pointer, not a copy. Practical limits are in the low kilobytes for an S7-1200 and tens of kilobytes for an S7-1500; profile the specific UDT against the target CPU's load memory in the TIA Portal resource view before locking the design.

Back to blog