Converting Siemens STL Conditional Jumps to LADDER in TIA Portal

David Krause12 min read
SiemensTIA PortalTutorial / 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

Overview

Statement List (STL) and Ladder Diagram (LAD) are both IEC 61131-3 programming languages supported by the SIMATIC S7-300, S7-400, S7-1200, and S7-1500 families. STL is text-based and exposes the CPU's accumulator directly through instructions such as L (Load), T (Transfer), AN (AND with NOT), and JC (Jump if RLO = 1). LAD is a graphical language that hides the accumulator and expresses logic through contacts, coils, and function blocks.

When porting legacy STL blocks into a TIA Portal project, the structural difference that most often confuses engineers is the conditional jump. A jump plus its label target appears as two lines in STL (JC Mxxx and Mxxx: NOP 0) but collapses into a single network in LAD because LAD has no native "goto" element. The label and the NOP 0 marker disappear; their semantic role is taken over by network boundaries and contact logic.

This article decodes the following STL fragment, which is a common pattern in legacy S7-300/400 FB and FC code, and converts it segment by segment into a clean LAD implementation that compiles in TIA Portal V15 and later.

      AN    "Tag_709"
      JC    M0A7
      L     %DBW10
      T     %DBW262
M0A7:NOP    0
      AN    "Tag_710"
      JC    M0B0
      L     %DBW20
      T     %DBW262
M0B0:NOP    0

Prerequisites

  • STEP 7 V5.5 or TIA Portal V13 SP1 or later. TIA Portal V15, V16, V17, and V18 all support the same STL-to-LAD conversion rules described here. See the Siemens KB 81318674 - Editing STL and LAD in parallel for editor capabilities.
  • A CPU that allows STL source. S7-1200 firmware V4.0 and later can hold STL blocks created in earlier versions, but new STL authoring is restricted on S7-1200. S7-1500 firmware V1.8 and later permits STL only if the CPU has been configured with the optional "Enable STL" setting. Confirm this in the CPU properties under Protection & Security > Permit access with PUT/GET and the project language settings.
  • Access to the data blocks referenced in the STL (DB10 and DB262 in the example) so the symbolic tags Tag_709 and Tag_710 can resolve to real addresses.
  • Familiarity with the Result of Logic Operation (RLO), Status Word (STW), and Accumulator 1 (ACCU1) concepts. The SIMATIC S7-1200 Programmable Controller System Manual (09/2021), section 6.3, covers the STL status word in detail.

STL Fundamentals Used in the Example

The RLO and how STL branches

Every STL instruction writes to the Result of Logic Operation bit in the status word. Bit logic instructions (A, AN, O, ON, X, XN) read an operand and combine it with the current RLO. AN "Tag_709" performs an AND-with-NOT: if Tag_709 is 0, the RLO is preserved; if Tag_709 is 1, the RLO is forced to 0. The RLO is the input to the next conditional instruction.

The JC jump instruction

JC <label> jumps to <label> if RLO = 1. If RLO = 0, execution falls through to the next sequential line. JC does not modify any register; it only changes the program counter. The destination <label> must be a jump label in the same block (an FB, FC, or OB).

The label and NOP 0 marker

A jump label is a four-character identifier made from the letters M and hexadecimal digits (M000 to MFFF). The label line itself contains NOP 0 (no operation with operand 0). NOP 0 is a real instruction that consumes one CPU cycle and writes nothing; its job is to provide a legal landing point for JC. The label and the NOP are not separate things - the label is the address of the NOP 0 instruction. Do not attempt to delete the NOP 0 when converting; LAD no longer needs it, but the STL source may still be present in archived blocks and must remain valid.

The L and T move pair

L %DBW10 reads a 16-bit word from data block DB10, offset 10 (bytes 10 and 11), and places it in ACCU1, shifting the previous ACCU1 into ACCU2. T %DBW262 copies ACCU1 (low word) into data block DB262, offset 262. Together they form an unconditional 16-bit copy that the LAD language calls MOVE (or the older MOV_W in the basic instructions panel).

Step-by-Step Conversion of the Example

Step 1 - Identify the program flow as if-else

Read the STL with the rule: "if the line above is JC and the line is between the jump and its label, the line is the body of an else branch." The two jumps in the example create two mutually exclusive paths:

STL fragment Logical reading
AN "Tag_709" / JC M0A7 / L %DBW10 / T %DBW262 / M0A7: NOP 0 If NOT Tag_709 then copy DBW10 into DBW262
AN "Tag_710" / JC M0B0 / L %DBW20 / T %DBW262 / M0B0: NOP 0 If NOT Tag_710 then copy DBW20 into DBW262

Note that the jumps are independent: a 1 on Tag_709 skips the first move but does not skip the second. The two segments form a sequence, not a nested selection.

Step 2 - Translate each segment into a LAD network

Place each segment into its own LAD network. Open the program block, switch the language to LAD, and add a new network per segment. From the Instructions pane, open Basic instructions > Bit logic operations and drag a normally-closed contact (--|/|--) and a MOVE box into the network.

Network 1

      Tag_709         DBW10       DBW262
 ---| |------( MOVE )-->  DBW
      (NC)        ENO         

Mapping:

  • --|/|-- is the LAD normally-open contact rendered as normally-closed by the NOT bubble; it is the direct translation of AN "Tag_709".
  • The (MOVE) box has input IN = %DBW10 (or the symbolic name if the DB is symbolic) and output OUT1 = %DBW262.

Network 2

      Tag_710         DBW20       DBW262
 ---| |------( MOVE )-->  DBW
      (NC)        ENO         

Same construction with Tag_710 and %DBW20.

Step 3 - Verify that no JC remains

LAD has no jump instructions. Once both networks are written, search the block source for the strings JC, JU, JCN, JL, JCB, JBI, JNB, JNBI, JO, JOS, JP, JM, JPZ, JMZ, JNZ, JZ. Any remaining hit is a translation error.

Step 4 - (Optional) Generate the SCL equivalent

If the project is migrating to S7-1500 and the team prefers structured text, the SCL is even shorter:

IF NOT "Tag_709" THEN
    "DB_262".Value := "DB_10".Value;     // word at offset 10
END_IF;

IF NOT "Tag_710" THEN
    "DB_262".Value := "DB_20".Value;     // word at offset 20
END_IF;

Why NOP 0 Disappears in LAD

Three reasons force the disappearance of NOP 0 and the label when converting to LAD:

  1. LAD has no program counter the user can target. The address of an instruction is implicit. The network is the atomic unit, and there is no "jump to this rung."
  2. Contact-coil logic always branches on the RLO. The normally-closed contact --|/|-- performs the same gate that AN + JC + label perform in STL. No placeholder is needed.
  3. NOP 0 is a placeholder, not behaviour. Removing it changes nothing observable. The first STL instruction after a label runs whenever execution reaches that instruction, which in LAD is "always, top to bottom".

If you must preserve the STL in the project (for example, for an audit trail or because the CPU's STL license is still in use), keep the original STL block untouched and add a new LAD block that calls into the same data. TIA Portal allows STL, LAD, FBD, and SCL blocks to coexist in one S7 program.

Common Pitfalls When Converting JC Chains

Symptom Root cause Fix
Output never updates even when the input is FALSE The AN was rendered as a normally-open contact because the programmer read "A" and forgot the N Use --|/|-- with the operand unchanged
Output updates on both segments simultaneously Both JC instructions were dropped instead of one being kept as a skip Make sure each JC collapses into a single normally-closed contact gating its own MOVE
Compiler error "Label M0A7 not defined" when reverting to STL Engineer edited the STL by hand and removed the NOP 0 line that the label pointed to Reinsert M0A7: NOP 0 on its own line
Code size doubles after conversion Each JC was converted to a separate branch that re-evaluates the condition Combine the two segments into one network only if they target the same MOVE; otherwise leave them as separate networks
Behaviour differs on first scan vs. subsequent scans First-scan flag (OB100 / startup bit) was treated like any other tag in STL but lost in LAD Place a normally-open contact on "FirstScan" as a separate parallel branch in the network

Edge Cases and Variants

JC versus JCN versus JU

The STL family of jumps includes at least these five forms on S7-300/400 and the same on S7-1500 with STL enabled:

STL Meaning LAD equivalent
JC label Jump if RLO = 1 Normally-open contact gating the body, inverted placement at the rung tail
JCN label Jump if RLO = 0 Normally-closed contact gating the body
JU label Unconditional jump No equivalent - re-architect as a separate network or subroutine call
JL label Jump distributor (case statement on ACCU1 low byte) Replace with a SCL CASE or LAD chain of equality contacts
JCB / JNB Jump with BR / RLO save Special case for FB/FC boundary handling; convert to a separate FC and call it conditionally

JC at the bottom of a chain (skip-to-end)

A frequent pattern is JC END at the bottom of a then block to skip the else body. The LAD conversion is two networks, with the second network placed inside a separate rung that has no contact on the variable that was just written. Example STL:

      A     "Start"
      JC    ENDIF
      L     %DBW10
      T     %DBW262
ENDIF:NOP    0

LAD version:

      Start           DBW10       DBW262
 ---| |--------------( MOVE )-->  DBW

Because the body of the if is the only thing the original STL executed when Start was 1, the LAD network is a single contact gating the move. There is no need for a second network; the absence of an else in the original STL means the move is simply not done.

JC inside a loop or block boundary

Jumps that cross network boundaries within a single block are legal in STL and illegal in LAD. The conversion must split the logic at the jump boundary, with one network per linear segment. Jumps that cross block boundaries (from one FB or FC to a label in another) are not legal in STL either - they are a separate error class with error code SF: 0x0800 in the diagnostic buffer ("Area length error when reading").

JC combined with accumulator instructions

If the body between the JC and the label sets ACCU1, the LAD version must use a coil or assignment that produces the same RLO and ACCU behaviour. Most commonly this is a SET or CLR coil, or a comparator --|CMP|==|-- box. Do not attempt to translate a complex accumulator pipeline into a single LAD network; break it into SCL.

Verification Procedure

  1. Static review. Open the LAD block and the STL block side by side. For every STL line that performs a write (T, S, R, =), confirm a matching coil or assignment exists in the corresponding LAD network. Count the number of writes; the numbers must match.
  2. Cross-reference. Use Show > Cross-references on the original STL tag %DBW262 and on the LAD output %DBW262. Both should list the same number of usage points across the program.
  3. Offline simulation. In TIA Portal, use PLC > Simulation > Start with S7-PLCSIM (or S7-PLCSIM Advanced for S7-1500). Force Tag_709 = 0 and observe %DBW262; it should equal %DBW10. Force Tag_709 = 1 and Tag_710 = 0 and observe %DBW262 = %DBW20. Force both to 1; %DBW262 should hold its last value.
  4. Online compare. With the actual CPU online, right-click the block and choose Compare > Online/offline. The block checksum and the interpreted STL view (right-click > Show in STL) should match the source STL within one or two NOPs introduced by LAD compilation.
  5. Trace recording. On S7-1500 with firmware V2.5 and later, configure a trace on Tag_709, Tag_710, %DBW10, %DBW20, and %DBW262. Record for at least 10 OB1 cycles and confirm the input/output relationship visually.

Performance and Cycle-Time Considerations

STL and LAD compile to the same MC7 or MC7+ machine code on the CPU. The cycle-time difference between an STL implementation with two JC instructions and a LAD implementation with two networks and two MOVE boxes is typically less than 1 microsecond on an S7-1516 and well below 10 microseconds on an S7-315-2. The dominant cost in this example is the data block access; %DBW10, %DBW20, and %DBW262 each cost one memory word load. Optimising by combining the two segments into a single network using a Variant MOVE_BLK would actually increase cycle time because of the additional block-move overhead. Keep the conversion 1:1 unless profiling proves otherwise.

Troubleshooting Matrix

Compiler / runtime event Likely cause Action
Build error: "Label expected" NOP 0 removed from a block still authored in STL Reinsert the line with its label
Build error: "Unknown instruction JC" CPU is configured for LAD/FBD/SCL only Enable STL in the CPU properties (S7-1500 only; S7-1200 has no STL)
Online: tag shows wrong value in HMI Symbolic tag was renamed in the new DB and the LAD still references the absolute address Re-link the symbol via PLC > Type > Update
SF LED on CPU, diagnostic buffer entry SF: 0x0070 / 0x0071 DBW262 is in a DB that is too short or uninitialised Open the DB, confirm length ≥ 264 bytes, recompile and download
Differences in PLC tag table after download STL and LAD blocks each declared their own version of the tag Consolidate the tag declarations into a single user-defined tag table
Watch table shows correct value, HMI shows stale HMI tag refresh cycle too slow relative to PLC scan Lower the HMI acquisition cycle in the HMI tag properties

FAQ

Why does my STL use JC plus a label that contains NOP 0, and what does NOP 0 do?

JC is a conditional jump. The label is a four-character identifier (Mxxx) attached to a NOP 0 instruction. NOP 0 is a real instruction that consumes one CPU cycle and changes nothing; its role is to provide a legal landing point for the jump. The label and the NOP are not two separate things - the label is the address of the NOP 0 line.

How do I convert AN "Tag" plus JC label plus L plus T into LAD?

Use one network with a normally-closed contact on "Tag" driving a MOVE box whose IN is the loaded word and OUT1 is the transfer target. The jump and label collapse into the contact; NOP 0 and the label are removed.

Can I keep the original STL block and add a new LAD block at the same time?

Yes. TIA Portal allows STL, LAD, FBD, and SCL blocks to coexist in a single S7 program. You can call the LAD block from the same OB1, or call the STL block from the LAD block and vice versa. Use this when you need a staged migration.

What if the body between JC and the label sets the accumulator (e.g. ADD, comparison)?

Break the segment into a separate network and use a coil, assignment, or comparator box. Do not try to force a multi-instruction accumulator pipeline into a single LAD network; convert that portion to SCL instead.

Why does my conversion work in PLCSIM but the real CPU reports SF: 0x0070?

Diagnostic event 0x0070 is "area length error when reading" and almost always means the target data block (DB262 in the example) does not have enough bytes to hold the offset you are writing to. Open the DB, confirm its length is at least 264 bytes, recompile, and download again.

Back to blog