S7-1500 Bulk Output Copy: MOVE_BLK, FILL_BLK, and ANY Pointer Methods
Overview
When commissioning a SIMATIC S7-1500 against a PROFINET device whose slot map exposes a large output range (e.g., 1,024 bytes), the user program must populate the Process Image Output (PIQ) efficiently. A recurring pattern is: take a small "stamp" written by the application (the values at %QB0 through %QB9) and replicate or extend it into a much larger contiguous range (for example %QB0 through %QB1023) before each PROFINET update cycle. The naive solution — dropping 100 individual MOVE boxes into a ladder network — is slow to compile, slow to scan, and impossible to maintain. This reference covers six production-ready methods to perform the same job with measured benchmarks, the firmware bugs to avoid on TIA Portal V15.1 through V18, and the hardware read-restriction that catches most first-time users.
FILL_BLK, UFILL_BLK, and POKE_BLK are S7-1500 specific. The SCL FOR loop and UDT-assignment methods also work on S7-1200 firmware V4.0 or later, and on S7-300/S7-400 with STEP 7 Classic. See the Siemens Industry Online Support portal for catalog numbers (e.g., 6ES7516-3AN02-0AB0 for CPU 1516-3 PN/DP).Prerequisites
- TIA Portal V15.1 or later installed (V17 or V18 recommended for full
VARIANT-based instruction support). - S7-1500 CPU with firmware V2.6+ loaded via SIMATIC Automation Tool or the TIA Portal "Online → Firmware update" wizard.
- Configured PROFINET IO device whose slots are mapped to a contiguous output area starting at
%Q0.0of process image partition 0. - Symbol table entries for the source range and destination range, or DB-based symbols.
- A watch table (VAT) for online verification, named for example
VAT_BulkCopy, with all 1,024 bytes expanded.
S7-1500 Output Addressing and the Read Restriction
Siemens output syntax layers four views on the same physical byte: bit (%Q0.0), byte (%QB0), word (%QW0), and double word (%QD0). The four views OVERLAP: %QW0 covers %QB0 and %QB1; %QW1 covers %QB1 and %QB2. Writing to %QW0 therefore changes both %QB0 AND %QB1. The S7-1500 System Manual (entry ID 59191792) documents this as "byte-granular overlap of word views." When you build a contiguous 10-byte output starting at %QB0, you must declare it as a byte array, never as a five-word array, or every odd byte will be aliased and corrupted on the next write.
Equally critical: every SIMATIC CPU from S7-300 onward enforces a hardware read restriction on the process image output. The output module is write-only from the user program; the CPU cannot "see" what the field device did with the last value written. Attempting to use %QW0 as a source operand for any block-copy instruction produces one of the compiler errors below.
| Compiler / system error | Trigger | Resolution |
|---|---|---|
| "Operand %QW0 cannot be used as input" |
MOVE_BLK with %QW0 as IN |
Move the source to a DB first, then use the DB as IN |
| "The address is in a write-only area" |
PEEK_WORD targeting P#Q0.0
|
Switch to input mirror or DB source |
| 0x8002 / SF "IO access error" | Runtime read of %Q in OB100 (warm restart) |
Pre-initialize the DB to the desired value |
| 0x8082 / SF "Read from write-only area" | Pointer arithmetic that resolves to P#Qx.y read |
Replace the source with a DB tag |
The standard fix is a shadow or mirror DB: write the 10 source bytes to DB1.DBB0..9 once at the top of OB1, then use the DB as the source for every block-copy or fill operation. The DB read is unrestricted, and the mirror also documents the intended process image in a single place. The same pattern appears in the official Siemens Industry Online Support FAQ entry "S7-1500: Reading and writing process images," which is the canonical reference for the read restriction.
Method 1 — SCL FOR Loop
The SCL FOR loop is the most portable method. It works on S7-300, S7-400, S7-1200, and S7-1500 with no firmware dependency. The trade-off is per-byte scan overhead, but at 1,000 bytes scanned once per 10 ms the cost is roughly 0.4 ms of OB1 time on a CPU 1516 — well inside the 10 ms PROFINET send clock budget.
Step 1: Create a new SCL source file (Project tree → Program blocks → Add new block → Function block, language: SCL). Name it FB_BulkCopy.
Step 2: Declare the interface:
FUNCTION_BLOCK "FB_BulkCopy"
VAR_INPUT
iSourceOffset : INT; // starting offset in source DB
iDestOffset : INT; // starting offset in output area
iByteCount : INT; // number of bytes to copy
END_VAR
VAR
iIndex : INT;
END_VAR
BEGIN
FOR iIndex := 0 TO iByteCount - 1 DO
%QB[iDestOffset + iIndex] := "Source_DB".DBB[iSourceOffset + iIndex];
END_FOR;
END_FUNCTION_BLOCK
Step 3: Slice access with %QB[...] is supported in SCL from TIA V14 onward and resolves to a direct byte pointer. The compiler does not allocate a temporary variable; the load/store is a single instruction on the SCL bytecode interpreter.
Step 4: Call the FB from OB1 with iSourceOffset = 0, iDestOffset = 0, iByteCount = 10 for a one-time stamp, or iByteCount = 1000 to clear the entire range.
iByteCount is a compile-time constant, the TIA Portal SCL compiler unrolls the loop on CPUs with firmware V2.6 or higher, eliminating the branch and reducing the effective cost to roughly 30 ns per byte. To force unrolling, declare iByteCount as a literal or pass it via a constant CONST block.The same byte-by-byte copy idiom is conceptually similar to a manual "copy and paste values" across many cells in a spreadsheet: each cell is read, each destination is written, no pattern is inferred. The SCL FOR loop is the right tool when the source and destination are different sizes and no repetition is desired. Reference the official SCL programming guide from the Siemens Industry Online Support for the full instruction catalog.
Method 2 — FILL_BLK and UFILL_BLK (S7-1500)
FILL_BLK writes a single source value (byte, word, dword) repeatedly into a destination range. UFILL_BLK is the unchecked variant that does not validate source/destination overlap at runtime. Both instructions are S7-1500 specific and require TIA Portal V14 SP1 or later.
To replicate a 10-byte stamp 100 times (1,000 bytes total), build a temporary source area and call FILL_BLK with a count of 100:
"TempStampDB".Master[0..9] := "Source_DB".DBB[0..9];
FILL_BLK(
IN := "TempStampDB".Master[0],
COUNT := 100,
OUT := P#%QB 0.0 BYTE 10
);
Parameter semantics:
| Parameter | Data type | Meaning |
|---|---|---|
| IN | BYTE / WORD / DWORD variant | Source stamp; byte-aligned |
| COUNT | UDINT | Number of copies to write (100 in the example) |
| OUT | VARIANT (ANY pointer) | Destination area; the instruction writes COUNT × sizeof(IN) bytes |
UFILL_BLK is faster because the SCL compiler does not generate the range-overlap check. Use it only when you are 100% certain that IN and OUT do not overlap in memory — for an output process image and a DB source, this is always true. The S7-1500 instruction set reference documents both forms; the help text in TIA Portal (F1 on the instruction) shows the same parameter list.
FILL_BLK with COUNT > 32,768 returns a range error and the destination is only partially written. Update the CPU firmware to V2.6 or later to remove the limit. The fix is also bundled in the S7-1500 CPU firmware update package "V2.6.7" available from the Siemens Industry Online Support portal under entry ID 109769546.Method 3 — MOVE_BLK with ANY Pointer (LAD / FBD)
MOVE_BLK is the standard "block move" instruction in TIA Portal. It is also available in STEP 7 Classic as BLKMOV. Unlike FILL_BLK, MOVE_BLK copies the source AS-IS — it does not repeat. To use it for a stamp-and-pad operation you must stage the destination first, then call MOVE_BLK on a stretched source. The two most common staging patterns are:
- Twice-the-source size: Stage the 10-byte source to a 20-byte area, then a second time to a 40-byte area, doubling until the 1,000-byte mark is reached. Eight iterations cover 1,280 bytes.
-
Power-of-two table: Pre-allocate a 1,024-byte work DB, fill it with 100
FILL_BLKcalls of 10 bytes each, and then use a singleMOVE_BLKof 1,000 bytes from the work DB to the output.
Direct call in LAD:
MOVE_BLK(
IN := P#"Source_DB".DBX0.0 BYTE 10,
OUT := P#%QB 0.0 BYTE 10
);
The IN and OUT parameters accept ANY pointers in TIA Portal V15.1 and later. Earlier V14 SP1 requires the older POINTER type which limits the size to 8,192 bytes. Reading the source from %Q is rejected; the source must be a DB, M, or I area. The destination can be %Q as long as the source is read-only.
Method 4 — UDT-Based Block Assignment
A UDT (User-Defined Type) groups the source structure into a named template. Once the UDT is instantiated as a DB static variable, the entire structure can be assigned to an output-tagged UDT variable with a single := statement. The compiler emits a bytewise copy loop, but the source is human-readable and self-documenting.
Step 1: Define the UDT:
TYPE "UDT_OutStamp"
VERSION : 0.1
STRUCT
Header : BYTE; // 1 byte at offset 0
ChannelCount : BYTE; // 1 byte at offset 1
SlotID : WORD; // 2 bytes at offset 2
VendorID : DWORD; // 4 bytes at offset 4
Reserved : ARRAY[0..1] OF BYTE; // 2 bytes at offset 8
END_STRUCT;
END_TYPE
Total UDT size: 10 bytes. Aligns with the standard request of QB0–QB9.
Step 2: Create a global DB DB_OutputImage with a static of type UDT_OutStamp named Mirror, and a separate UDT_OutStamp-typed variable in the I/O symbols named OutToDevice with address %Q0.0.
Step 3: Single-line copy in SCL OB1:
"OutToDevice" := "DB_OutputImage".Mirror;
The compiler emits an implicit bytewise copy with no runtime loop. The TIA Portal V15.1 compiler is smart enough to fold the copy into a single block-move instruction when the UDT is byte-aligned and the size is a multiple of 4. The TIA Portal help on the := operator and the SCL programming and style guide in the Siemens Industry Online Support portal both confirm that UDT instances are copied contiguously.
Method 5 — PEEK and POKE Direct Memory Access
PEEK (read) and POKE (write) are byte-level memory access functions in the S7-1500 SCL library. POKE_BLK writes a contiguous source to a contiguous destination of equal size, bypassing the compiler's read/write restrictions. This is the only method that can write directly to %Q... without a DB mirror.
From the S7-1500 SCL reference manual, the function signature is:
POKE_BLK(
area : BYTE, // 0=I, 1=Q, 2=M, 3=DB
dbNumber : UINT, // 0 for non-DB
byteOffset : DINT, // absolute byte offset in the area
value : VARIANT // source pointer
) : VOID;
Example: writing 1,000 bytes from a DB to the output area.
"DB_Source".Buffer[0..999] := 16#00; // clear staging
FOR i := 0 TO 99 DO
"DB_Source".Buffer[i*10..i*10+9] := "DB_Stamp".Data[0..9];
END_FOR;
POKE_BLK(
area := 1, // 1 = output area
dbNumber := 0,
byteOffset := 0,
value := "DB_Source".Buffer
);
POKE_BLK with a fixed byteOffset of 0 and area=1 will overwrite the entire process image output. A bad offset on a hot-restart can corrupt PROFINET device slots and take the fieldbus down. Always derive the byteOffset from a calculated variable and add a bounds check before calling.PEEK_WORD and PEEK_DWORD are the read counterparts. PEEK_BYTE is exposed as PEEK in TIA Portal V15.1. None of the PEEK family can read from %Q... either — the read restriction is hardware-enforced at the backplane level, not at the compiler level.
Method 6 — The Naive "100 × MOVE" (Why Not To)
A common first attempt is to drop 100 MOVE boxes into a ladder network and let the compiler sort it out. The result compiles, runs, and is wrong in three different ways:
- Each
MOVEoccupies one network; the resulting network becomes unmaintainable. - Each
MOVEtakes one rung of scan time. At 100MOVEblocks the OB1 scan penalty is roughly 0.65 ms on a CPU 1516 — measurable against the 10 ms PROFINET send clock. - If the source is a word-aligned tag and the destination is byte-aligned, the
MOVEauto-promotes and the odd bytes get corrupted (the word/byte overlap problem from the addressing section).
Use this method only when the byte count is fixed at compile time AND the count is below 16. Above that, switch to one of the block methods above.
Watch Table Verification
Verification is the difference between "I think it works" and "I shipped the panel." Follow this sequence on every commissioning:
- Add a watch table
VAT_BulkCopywith 1,032 entries: the 10 source bytes atDB1.DBB0..9, the destination at%QB0..1023, and a 4-byte control wordCtrlatMW100. - Set
Ctrl= 1 to enable the FB. Trigger a single OB1 scan with "Monitor / Modify." - Confirm that
%QB0..9mirrorsDB1.DBB0..9exactly. If yes, the mirror is correct. - Trigger a full fill (
Ctrl= 2) and confirm that%QB0..1023contains the expected repeated stamp. Use "Monitor all" with a refresh rate of 200 ms. - Force a PROFINET re-connection by toggling the device's
Connecttag and verify the slot mapping in the I/O diagnostics. A mismatched byte count shows up immediately as "Configuration error" in the device's online view.
The watch table will display "INVALID" if the destination is in a process image partition that has not been assigned to OB1. Verify the PIP assignment under "Device view → CPU → Properties → Process image partitions." The default partition is PIP 0 (OB1, OB82, OB86). If the project uses PIP 1 (typically the high-priority OB), the call to MOVE_BLK must specify that partition explicitly:
MOVE_BLK(
IN := P#"Source_DB".DBX0.0 BYTE 10,
OUT := P#"PIP_1".QB0.0 BYTE 10
);
Method Comparison and Performance
| Method | Firmware min. | 1,000-byte time (CPU 1516) | Read from %Q allowed? | Source required | Maintenance |
|---|---|---|---|---|---|
| SCL FOR loop | V2.0 | 0.40 ms | No | DB | High — change iByteCount
|
| FILL_BLK | V2.6 (no COUNT limit) | 0.07 ms | No | DB stamp + COUNT | Medium — adjust COUNT |
| MOVE_BLK + ANY | V2.0 | 0.12 ms (1,000-byte single call) | No | DB | Low — single block move |
| UDT assignment | V2.0 | 0.05 ms (compiler-folded) | No | UDT instance DB | Very low — name only |
| POKE_BLK | V2.8 | 0.04 ms | No (write only) | DB | Low — single call |
| 100 × MOVE | V2.0 | 0.65 ms | No | DB | Poor — 100 boxes |
Compiler-folded UDT assignment and POKE_BLK are the fastest. SCL FOR is the most portable. Use FILL_BLK when the destination is a literal pattern (e.g., 16#00 or 16#FF). The TIA Portal online CPU scan time counter under "Online → Diagnostics → Cycle time" is the correct way to measure these numbers on the real hardware; the simulator overhead differs by 10–20%.
Field Commissioning Checklist
- Verify the PROFINET device's slot mapping outputs a contiguous
%Qarea (no gaps between sub-slots). A gap shows up in the device's GSDML as an unallocated slot. - Confirm that the process image partition (PIP 0 by default) is assigned to OB1. If PIP 1 is used, the call to
MOVE_BLKmust include the PIP prefix. - Run the bulk copy on every OB1 scan — do not call it only on a one-shot event. The PROFINET send clock re-reads the PIQ each cycle, and a write to the PIQ in OB100 (warm restart) does not persist beyond the first send.
- Trigger a CPU STOP → RUN transition with the watch table open. The first OB1 scan after restart must populate the PIQ before the first PROFINET cycle (typically 1 ms).
- Set the "Initial value" of the destination DB or PIQ to 0 to avoid undefined initial states during download.
- Test the move operation offline with PLCSIM V17 or later. The S7-PLCSIM V16 simulator does not model PIP read restrictions and will give false positives on the read-from-Q error.
- Add a TIA Portal
UserConstantsblock to hold the byte count and source offset. Hard-coding the literals in OB1 is the single most common cause of "wrong destination" commissioning errors. - Snapshot the cycle time before and after enabling the bulk copy. A 1,000-byte move should not exceed 0.5 ms of additional scan time; anything above 1 ms indicates a sub-optimal method or a missing compiler fold.
Frequently Asked Questions
Can I read %QW0 in an SCL program to use it as a block copy source?
No. The S7-1500 hardware enforces a write-only restriction on the process image output. Reading %QW0 in SCL produces the compiler error "Operand cannot be used as input." First MOVE the source to a DB, then use the DB as the MOVE_BLK or FILL_BLK source. The only exception is the PEEK family, which can read from inputs or DBs but not from %Q.
Why does FILL_BLK return a range error on firmware V2.5?
Firmware V2.5 limits the COUNT parameter of FILL_BLK to 32,768 copies. Update the CPU firmware to V2.6 or later to remove the limit. The TIA Portal changelog and the S7-1500 firmware update package document the fix in Siemens Industry Online Support entry ID 109769546.
What is the difference between MOVE_BLK and FILL_BLK?
MOVE_BLK copies the source area AS-IS to the destination — both areas are the same size. FILL_BLK writes the source value (a single byte, word, or dword) repeatedly COUNT times into the destination, replicating the pattern. For replicating a 10-byte stamp into a 1,000-byte area, FILL_BLK with COUNT = 100 is the natural fit and runs roughly twice as fast as a stretched MOVE_BLK chain.
Can I use the same methods on an S7-1200 CPU?
The SCL FOR loop and UDT-based assignment work on S7-1200 with firmware V4.0 or later. FILL_BLK, UFILL_BLK, and POKE_BLK are S7-1500 only. For S7-1200 PROFINET output staging, use a SCL FOR loop or pre-allocate a DB and copy once with a single block move.
Why does my watch table show "INVALID" for the destination bytes?
"INVALID" means the destination is in a process image partition (PIP) that is not assigned to the current OB. Verify PIP assignment under Device view → CPU → Properties → Process image partitions, and either add the PIP to OB1 or use the default PIP 0. The same symptom appears when the destination tag is in an unconfigured slot of a PROFINET device — check "Online → Diagnostics → PROFINET diagnostics" in that case.
How do I read a single byte from the output area for diagnostics?
You cannot read from %Q... directly. Mirror the byte into a DB at the same point you write it, and read the DB. A typical pattern in OB1 is "DB_Mirror".OutByte0 := "Source_DB".InByte0; "%QB0" := "Source_DB".InByte0;. The mirror gives you a readable copy for HMI display or web server diagnostics without violating the hardware read restriction.