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 |
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 constantCONSTsymbols declared in its own interface. - The block may only read/write
INPUT,OUTPUT,IN_OUT, andSTATparameters 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
STATinstance 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 |
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:
-
Hoist globals into the interface. Move global constants, configuration values, and machine parameters into
INPUTorIN_OUTparameters. The caller passes the value in. -
Localize DBs. Replace global DB reads with a single
IN_OUTparameter typed as a UDT. The caller passes a DB element of that UDT type. -
Move multi-instance FBs into the KHP block's STAT. Instead of calling
"Recipe".FB_MotorCtrl(...)on a globally declared FB, declarei_MotorCtrl : FB_MotorCtrlin the STAT area of the KHP FB and calli_MotorCtrl(...). - 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.
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:
- 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.
- 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.
- 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.
- 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.
-
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 accessmessage. - 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 |
"[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.