Finding Function Block Usage in TIA Portal: Cross-Reference Guide
Locate every call site of a Siemens Function Block (FB) inside a STEP 7 / TIA Portal project using the engineering tools that ship with the IDE: Cross-References, Call Structure, Dependency Structure, the Go to Usage context command, and the project-wide search. This guide consolidates the workflow for TIA Portal V16 through V19, explains why local tags (which always start with #) never appear in cross-reference output, and provides a verification matrix for engineers who must confirm that a given FB is wired, called, instanced, or genuinely orphan before deletion or refactoring.
1. Overview: The Block-Usage Problem in TIA Portal
Function Blocks (FBs) in STEP 7 are reusable code containers that retain their static instance data between scan cycles. Once a project grows beyond a single OB and a handful of FBs, locating every call site of a specific block (for example, FB35 holding alarm-horn logic) becomes non-trivial. TIA Portal exposes four distinct views of block usage, each answering a different engineering question:
| View | Engineering question answered | Typical use | Menu path |
|---|---|---|---|
| Cross-References | Where is this symbol/block used as an operand? | Renaming impact, deletion safety, I/O sanity | Right-click block → Cross-references |
| Call Structure | What is the call hierarchy? (OB → FB → FB → FC) | Understanding execution flow, scan-time analysis | Project tree → PLC → Reference data → Call structure |
| Dependency Structure | Which instance uses which type? (Multi-instance + global DBs) | Type-instance relationships, library blocks | Project tree → PLC → Reference data → Dependency structure |
| Go to Usage (context) | Where is the symbol under the cursor referenced? | Quick navigation while editing | Right-click symbol → Go to → Usage |
| Project-Wide Search | Find literal strings, symbol names, or addresses | Recovery when reference data is stale | Ctrl+F (in the project view) |
Many engineers first try only the Cross-Reference window and conclude that the block in question is unused. That conclusion is frequently wrong because FBs are typically called from other blocks, and call relationships are surfaced in the Program Structure view, not in the standard Cross-Reference row layout. The default filter on the reference window is "Cross-references", which lists operand accesses (variable reads and writes). The Program Structure filter must be selected and the sub-mode switched from "Call structure" to "Dependency structure" in order to see the type-instance call tree.
2. Prerequisites and Project Setup
Before querying block usage, verify the following conditions. Skipping any of them can produce empty or misleading results, especially in large or multi-user projects.
- Project is compiled. Reference data is generated during the "Compile" step (hardware + software). If the project has not been compiled since the last code change, every usage view will be stale or empty. Trigger a full rebuild via Project → Compile → All (rebuild).
- Reference data is refreshed. In the project tree, right-click the PLC station and choose Reference data → Generate. The resulting dialog shows timestamp and last-refresh reason.
- The block is in the project tree. Open Project navigation → PLC → Program blocks and confirm that FB35 (or the block of interest) is visible and not greyed out. Greyed blocks are typically system-provided or in a read-only library.
-
For S7-1200/S7-1500: Optimized block access does not affect cross-references, but know that block-internal temporary and static variables (the
#-prefixed tags) never appear in any cross-reference output. This is by design — the variable is local to its enclosing block. - For library master copies: Reusable master copies of FBs reside under Project library and have no instances until they are used. Cross-references inside a master copy return zero hits until at least one instance is created in a program.
3. Method 1: The Cross-Reference Window (Operand-Centric)
The Cross-Reference window is the first tool most engineers reach for. It lists every location where a symbol is read, written, or both. For FBs, however, the symbolic name of the FB only appears as an operand when the FB is referenced symbolically (for example, by passing it as an InOut parameter). The more common case — an FB being called from another block — shows up in the Cross-Reference window as a call row rather than a read/write row.
Step-by-step
- Open the project tree and expand PLC → Program blocks.
- Right-click the FB you want to trace (for example, FB35).
- Select Cross-references from the context menu. The reference window opens in the work area.
- In the reference window, click the funnel-shaped Filter button in the toolbar.
- In the filter dialog, leave the "Cross-references" row enabled and click OK. This is the default state.
- Examine the result list. Each row shows: block name, line number, access type (read/write/call), and the symbolic address or call argument.
If the FB is called from another block (for example, OB1 contains CALL FB35, DB35), the call will appear as a single row with access type "Call" and the target block name. If the FB is only called from inside another FB (a common pattern for sub-routines such as the horn logic in the field report), and the parent FB is itself called from an OB, then the call chain reads:
OB1
→ FB100 (main sequence) -- Cross-Reference shows CALL FB100
→ FB35 (horn logic) -- Cross-Reference shows CALL FB35 inside FB100
To reveal the full chain, click on the parent block in the result list. TIA Portal jumps to the editor and highlights the CALL statement. From there you can use Go to → Definition to step into the called block.
| Access type column | Meaning | When does it appear for an FB? |
|---|---|---|
| Read (R) | Symbol is read | FB passed as InOut to another block |
| Write (W) | Symbol is written | Rare for FBs; possible when an instance DB is written directly |
| Call (C) | Block is invoked | Every CALL statement, multi-instance parent, and used library block |
4. Method 2: Program Structure - Call Structure View
The Call Structure view presents a tree of every block invocation chain starting from each OB. This is the right tool when the engineering question is "what runs at runtime, and in what order?" It is also the most reliable indicator of whether a given FB is reachable from the cyclic task.
Step-by-step
- In the project tree, right-click the PLC station.
- Choose Reference data → Generate (only required if not already generated; skip if the menu item is greyed out, which means data is current).
- Expand PLC → Reference data.
- Double-click Call structure. The reference window opens with the Program Structure filter pre-selected.
- In the toolbar, click Filter and confirm the dropdown is on Program Structure with sub-mode Call structure.
- The left pane shows a collapsible tree: OB1 → FB100 → FB35 → .... Each level expands with a + sign.
Call Structure answers the question "is FB35 reached from any OB during the scan?" If FB35 does not appear under any OB subtree, then either it is unused, or it is called indirectly through a mechanism that Call Structure does not model (see Section 9 on multi-instance and library edge cases).
TCON-style dynamic block identifiers, or calls hidden behind WRIT_DBL in older STEP 7 classic, do not appear. For S7-1500 with the optional "Dynamic call" feature enabled in the CPU properties, Call Structure may show a placeholder node with a warning icon.5. Method 3: Program Structure - Dependency Structure View (The Key Fix)
The Dependency Structure view is the one that solves the original problem described in the field report. It visualizes the type-instance relationship: which FB type is used by which FB instance, including multi-instances and global instance DBs.
In a Call Structure view, the trace might be "OB1 → FB100 → ??" with no further information if FB100 declares FB35 as a multi-instance (a static variable of type FB35 declared in the static section of FB100). The Dependency Structure view exposes that relationship because it models block types rather than call invocations.
Step-by-step
- Open the reference window as in Method 2.
- Click the Filter button in the toolbar.
- In the filter dialog, switch the sub-mode from Call structure to Dependency structure.
- Press OK.
- The left pane now shows block types as parent nodes, with each instance or type-consumer as a child node. Locate FB35 in the tree.
- Expand the FB35 node. Every block that contains a static instance of FB35 (multi-instance) or that holds a global instance DB of FB35 is listed as a child.
This is the single most common answer to the question "where is FB35 used?" in a project organized with multi-instances. A typical dependency tree might look like this:
FB35 "Horn_logic"
↑ used as multi-instance in
FB100 "Alarm_sequence" (static: hornInst : FB35)
↑ called from
OB1 "Main"
Once you identify the parent block (FB100 in the example), you can right-click it and choose Cross-references to find where FB100 is called, completing the chain back to the OB.
6. Method 4: Go to Usage (Context Menu, Fastest for Active Editing)
When you are already inside a block editor and want to know where a specific tag, instance, or block call is used, the Go to Usage command is the fastest path.
Step-by-step
- Open the block containing the FB call or the static instance (for example, FB100).
- Click on the variable name in the editor. The variable could be a CALL target (e.g.,
hornInst), a multi-instance variable, or a tag passed by reference. - Right-click the selection. The context menu opens.
- Choose Go to → Usage. The cross-reference list for the selected symbol appears in a pop-up panel.
- Double-click any result row to jump to that location.
For the horn-logic scenario: open FB100, find the line that reads CALL #hornInst (where #hornInst is the multi-instance of FB35). Right-click #hornInst and choose Go to → Usage. The list will include the call site in FB100 and any other place the same instance is referenced. This works even when the local tag is not visible to a global cross-reference (see Section 8).
7. Method 5: Project-Wide Text Search
The reference data views all depend on a successful compile. If the project has compile errors, or if you are working with an unprotected copy that has never been compiled, every usage query returns empty. In that situation, a brute-force text search is the fallback.
Step-by-step
- Press
Ctrl+Fin the project tree (or use Edit → Find and replace). - Enter the literal search string. For an FB, search for the FB number prefixed with "FB" (e.g.,
"FB35"), or for a symbolic name (e.g.,"Horn_logic"). - Set the search scope to Entire project and the search target to Source code (SCL/LAD/FBD).
- Run the search. Each match shows the file, block, and line number.
This method is slower than the reference data views for large projects (tens of thousands of lines), but it works on uncompiled code, library source files, and external SCL sources. Combine it with Find in files results filtering by file extension *.scl and *.awl to reduce noise.
8. Understanding Local Tags (# Prefix) and Why They Never Appear
In STEP 7 and TIA Portal, every block has a name and a number. Tags declared in the static, temp, or in/out interface sections of a block are local to that block and carry the literal prefix # in the editor (for example, #startButton, #hornInst). The # is a syntactic marker; the tag does not exist in the global symbol table, and it has no scope outside its parent block.
The cross-reference views in TIA Portal are designed to expose global references — the data that the rest of the project, the HMI, the OPC UA server, or another controller can see. By design, local tags are deliberately excluded. This is why the field report notes that "local tags can't be seen in cross reference." The rule is:
- Global tag (PLC tags table) → appears in Cross-References.
- Block I/O pin (In/Out/InOut) of an FB → appears in Cross-References at the call site, attributed to the parent block's CALL statement.
-
Static / Temp / Constant local (with
#prefix) → never appears in Cross-References. Use Go to Usage (Section 6) from inside the owning block, or use project-wide text search (Section 7).
CALL #hornInst inside FB100, then the multi-instance variable #hornInst is local to FB100 and invisible to the global Cross-Reference. The call itself is still tracked — the Cross-Reference window shows "FB100 uses FB35" because the compiler records the type-instance edge in the reference database. The Dependency Structure view (Section 5) is the cleanest way to see this relationship explicitly.9. Multi-Instance, Global DB, and Library Block Edge Cases
The four standard methods above cover roughly 90% of block-usage queries. The remaining 10% involve advanced constructs that require interpretation. The following table summarizes the edge cases and the recommended diagnostic:
| Construct | Where it surfaces in Call Structure | Where it surfaces in Dependency Structure | Recommended method |
|---|---|---|---|
| Multi-instance (FB containing FB as static) | Only as the call site; not as a child of the parent OB | Explicit parent-child edge | Dependency Structure |
| Global instance DB (DB of type FB) | Yes, as a call from the OB/FB that owns the CALL | Yes, as a child of the FB type | Either view, plus Go to Usage |
| Library master copy (not yet used) | Does not appear (never compiled into a program) | Does not appear | Project tree → Project library → right-click → Types in use |
| Library type with typed instance in another project | Only after re-compile with the global library in scope | Only after re-compile | Re-compile, then re-query |
| Indirect call via VARIANT or block-container | May show as a placeholder with warning | Does not model indirection | Manual inspection of the calling block |
| OB that is not assigned to a task | Does not appear (never scheduled) | Does not appear | PLC properties → Task configuration |
| S7-1500 Motion Technology blocks (TO, MC_Power, etc.) | Appear under the Motion task OB | Appear under their FB type | Standard methods, plus the Technology objects tree |
A common pattern in mature S7-1500 code is to wrap a vendor library FB in a project-specific wrapper FB that exposes only a subset of the interface. In that case, the wrapper FB is what the application code calls, and the underlying library FB is called only by the wrapper. Tracing the library FB through Call Structure stops at the wrapper; Dependency Structure then shows the wrapper as a type-consumer of the library FB. This is the same multi-instance concept generalized to a global DB consumer.
10. Version Differences and Project-Size Performance
Reference data generation is a compile-time analysis pass. On large projects (several thousand blocks, deeply nested), the pass can take minutes. The behavior differs across TIA Portal versions:
| TIA Portal version | Reference data behavior | Workaround for missing results |
|---|---|---|
| V13 / V13 SP1 | Bug: cross-reference does not enumerate multi-instances consistently | Upgrade to V14 SP1 or later; otherwise manual grep |
| V14 / V14 SP1 | Dependency Structure introduced; works reliably for S7-1500 | None required for S7-1500 |
| V15 / V15.1 | Stale-data bug after incremental compile; fix by full rebuild | Full rebuild (Project → Compile → All) |
| V16 | Stable; integrates with the unified TIA Portal help system | None |
| V17 | Adds "Used in" column in the Info window for blocks | None |
| V18 | Performance improvements for > 10,000 blocks; supports offline & online diff | None |
| V19 (current) | Adds traceability to PLM data; faster dependency analysis | None |
For projects exceeding ~5,000 blocks, the dependency tree can become too wide to navigate. Use the Search box inside the reference window (top-right) to filter the visible nodes by name. Typing FB35 in that box isolates the FB and its consumers, collapsing all unrelated branches.
11. Verification Checklist and Engineering Workflow
Before claiming that an FB is unused, walk this checklist. Each step must return a positive result for the block to be considered safe to remove or refactor.
- Cross-Reference window → expected: zero or more "Call" rows.
- Call Structure view → expected: FB not reachable from any OB subtree.
- Dependency Structure view → expected: FB node has no children, and no parent block declares it as a multi-instance.
- Go to Usage from any block suspected of containing a hidden multi-instance → expected: no result rows.
-
Project-wide text search for the FB number and FB name in
*.scl,*.awl,*.lad,*.fbd→ expected: zero matches. - Project library → Types in use → expected: the FB does not appear in the "Used in program" column.
- Compile warnings → expected: a warning tagged "Unused block" exists in the Info window. Absence of a warning is ambiguous (it can mean "used" or "warning suppressed"), so do not rely on it alone.
- Online / offline diff (TIA Portal V18+) → expected: no live online instance is present on the target CPU that would be orphaned by a removal.
If all eight steps return the expected outcome, the FB is a candidate for removal. Confirm with a peer review, then delete or move it to a holding library. A safe practice is to set the FB to know-how protected with a placeholder body for one production cycle and verify that no field fault appears before physical deletion.
12. Troubleshooting Matrix
| Symptom | Likely cause | Resolution |
|---|---|---|
| Cross-Reference is empty for a known-called FB | Stale reference data; incremental compile missed updates | Project → Compile → All (rebuild), then regenerate reference data |
| Call Structure shows the FB as a leaf but no parent | FB called only as a multi-instance | Switch filter to Dependency structure |
| Dependency Structure does not list the FB at all | FB truly unused, or inside a non-compiled library | Compile project library, then re-query; if still empty, the FB is orphan |
| Go to Usage shows nothing for a tag named like a global | Tag is local (prefixed #); by design not in cross-reference |
Use Go to Definition from the editor, or text search |
| Reference window hangs for minutes | Large project + know-how-protected blocks | Temporarily disable know-how protection; analyze; re-enable |
| Search returns hits in compiled SCL but not in the source SCL | Compiler-generated DB initial values mention the FB number | Filter search to source files only (*.scl in Sources folder) |
| OB does not appear in Call Structure | OB is not assigned to any cyclic, time-of-day, or interrupt task | PLC properties → Task configuration; assign OB |
| Library block disappears after project re-open | Library reference was removed or path changed | Re-add the library in Project library → Manage |
| Compile error "Block is not called" appears after deletion | Static instance of a multi-instance block was not removed | Remove the multi-instance declaration from the parent block's static section |
| Cross-reference shows wrong line numbers | Project was edited without an intermediate compile | Save all, then full rebuild |
Why does my FB not appear in the Cross-Reference window even though I know it is called?
The default Cross-Reference filter lists operand accesses (reads, writes), not block calls. Switch the reference window filter to Program Structure and select Call structure or, for multi-instances, Dependency structure. Calls to FBs from other blocks show up there, not in the operand-only view. Also confirm that reference data has been regenerated after the latest compile (right-click the PLC → Reference data → Generate).
What is the difference between Call Structure and Dependency Structure in TIA Portal?
Call Structure shows the runtime call hierarchy: which block invokes which other block, starting from each OB. Dependency Structure shows the type-instance relationship: which FB type is used by which parent block (including multi-instances and global instance DBs). For tracing a sub-routine FB invoked as a multi-instance, Dependency Structure is the correct view; Call Structure will not show the parent-child edge.
How do I find usage of a local tag (one prefixed with #)?
Local tags are intentionally hidden from global cross-reference data. Use Go to → Usage from the editor after clicking the tag, or perform a project-wide text search (Ctrl+F) for the tag name. Local tags only exist within their declaring block, so the result set is bounded by the size of that one block.
Can I find where a reusable library FB is used across the project?
Yes. Open Project library in the project tree, right-click the master copy, and choose Types in use. The resulting dialog lists every program that contains an instance of the FB. Cross-reference queries against the same FB inside an active program also work as long as the program has been compiled since the library was added.
How do I export the cross-reference list to Excel for documentation?
In the reference window, click the funnel-shaped Filter button to set the desired view, then right-click anywhere in the result list and choose Export → CSV (or Export to clipboard). Paste the clipboard contents into Excel, or open the CSV directly. For TIA Portal V17 and later, the Export → Excel option preserves the column structure without further formatting.