PLC Simulator
Program flow

JSRJump to Subroutine

JSR (Jump to Subroutine) transfers program execution to a named routine; the routine runs top to bottom, an explicit or implicit RET returns execution to the instruction after the JSR — every scan the rung is true.

Join 1900+ learners practicing PLC programming

How it works

What JSR does during the scan

Without subroutines, a PLC program is one enormous routine scanned start to finish. JSR is how programs grow structure: the main routine becomes a table of contents — a rung per machine section, each conditioning a JSR to a routine that owns that section. When the scan reaches a true JSR rung, execution jumps into the named routine, runs its rungs top to bottom, hits the return (an explicit RET or simply the end of the routine), and resumes at the rung after the JSR. The subroutine’s rungs consume scan time only when actually called.

The conditioning is the point — and the trap. A JSR with conditions is a scan switch: while the rung is false, the routine is not scanned at all. Not scanned as false — not scanned. Every OTE inside keeps whatever value it last wrote; timers stop accumulating but hold their ACC; nothing in the routine updates. An output left ON when its routine stopped being scanned stays ON, indefinitely, while the code that should turn it off is skipped. This is one of the most common causes of "the machine kept running with the mode switched off". The safe idioms: call routines unconditionally and gate the logic inside them, or make the mode logic explicitly clean up (a "last scan" pass that de-energizes the routine’s outputs).

In Studio 5000, JSR can also pass data: input parameters are copied into the SBR instruction’s operands on entry, and the RET instruction can hand return parameters back. This turns a routine into a reusable, almost function-like block — the same conveyor-control routine called three times with three different tag sets. (For heavy reuse, Logix’s Add-On Instructions go further, but parameterized JSR/SBR/RET is the ladder-native version.)

Nesting is allowed — a subroutine may JSR into another — with a platform-specific depth limit, and recursion is a fault, not a technique. Other platforms draw the same picture with different names: Siemens programs are built from CALLed FCs and FBs (with UC/CC in legacy STL), the IEC 61131-3 languages invoke functions and function blocks directly, and Mitsubishi ladder uses CALL Pn / SRET with pointer labels.

JSR jump-to-subroutine instruction highlighted on a ladder rung — an Auto_Mode contact conditioning a JSR block that calls the Pump_Control routineA ladder rung between power rails: an XIC contact tagged Auto_Mode drives the highlighted JSR block whose parameter names the routine Pump_Control. While the rung is true, every scan jumps into the subroutine, executes it, and returns to the rung below the JSR.L1L2Auto_ModeXICJSRRoutine:Pump_Controlcalls the routine while true
The JSR (highlighted) is conditioned by Auto_Mode: while true, every scan detours into the Pump_Control routine and returns to finish the calling rung’s routine.
JSR call flow diagram — the main routine scan reaches a JSR, jumps into the Pump_Control subroutine, executes it top to bottom, and RET returns execution to the rung after the JSRTwo program blocks connected by arrows. Left: the MainRoutine block with rungs listed top to bottom and a JSR Pump_Control rung in the middle. Right: the Pump_Control subroutine block starting with SBR, its own rungs, and RET at the bottom. A forward arrow labelled call goes from the JSR rung to SBR, and a return arrow labelled return goes from RET back to the rung below the JSR.MainRoutinerung 0 …rung 1 …JSR Pump_Controlrung 3 …rung 4 …Pump_ControlSBRrung 0 …rung 1 …rung 2 …RETcallreturnunscanned routines freeze their outputs at last state
Call flow: the main routine reaches the JSR, execution jumps to the subroutine (SBR → rungs → RET) and returns to the rung after the JSR — once per scan while the rung is true.

Across vendors

JSR in Allen-Bradley, Siemens, IEC 61131-3 and Mitsubishi

PlatformName / syntaxNotes
Allen-Bradley (Studio 5000 / RSLogix)JSR / SBR / RETJSR names the routine and optionally passes input/return parameters; SBR receives inputs; RET returns (with values). Routines not called are not scanned.
Siemens (TIA Portal)CALL FC / FBBlocks are called with parameter interfaces; FBs carry instance DBs (state), FCs are stateless. Legacy STL also has UC/CC unconditional/conditional calls.
IEC 61131-3 (CODESYS, OpenPLC)Function / FB invocationPOUs call functions and function block instances directly (graphically in LD/FBD, or name(args) in ST). Task configuration decides what runs each cycle.
Mitsubishi (GX Works)CALL Pn / SRETCALL P10 jumps to pointer label P10 after the FEND of the main program; SRET returns. Nesting is allowed to a fixed depth.

The universal rule hiding under all four syntaxes: code that is not called does not execute, and whatever its outputs were doing last, they keep doing. Structure your calls so that "not called" is always a safe state.

In practice

Worked JSR examples

Example 1 — main routine as a table of contents

|                                        JSR Safety      |
|------------------------------------------[JSR]--------|

|                                        JSR Conveyors   |
|------------------------------------------[JSR]--------|

| Auto_Mode                              JSR Auto_Seq    |
|----] [-----------------------------------[JSR]--------|

| Auto_Mode                              JSR Manual_Ctrl |
|----]/[-----------------------------------[JSR]--------|

Safety and conveyor logic are called unconditionally — they must run every scan, in every mode. The automatic sequence and manual controls are mutually exclusive, gated by Auto_Mode. Note what this structure implies: any output written inside Auto_Seq must be safe to freeze when the mode drops, or must also be handled in Manual_Ctrl. The cleanest designs keep actuator-driving OTEs in an always-scanned routine and let the mode routines set request bits.

Example 2 — one routine, three stations (parameterized JSR)

|                        JSR Clamp_Station               |
|----------------------[ Input par:  Stn1_Sensors     ]--|
|                      [ Return par: Stn1_Cmds        ]--|

|                        JSR Clamp_Station               |
|----------------------[ Input par:  Stn2_Sensors     ]--|
|                      [ Return par: Stn2_Cmds        ]--|

Two JSRs call the same Clamp_Station routine with different parameter sets: the SBR at the top of the routine receives the station’s sensor data, the logic runs once per call, and the RET hands back that station’s commands. Fix a bug once, both stations get the fix.

The limitation to respect: the routine’s own local tags are shared between calls within the scan, so anything stateful (timers, one-shot storage bits) must live in the per-station data, not in the routine — otherwise station 2 inherits station 1’s edge memory. This is exactly the problem Add-On Instructions later solved with true instances.

Gotchas

Common JSR mistakes

  • Conditioning a JSR without handling the frozen outputs

    When the rung goes false the routine simply stops being scanned — every output inside keeps its last value. A conveyor started inside the routine keeps running. Either call unconditionally and gate inside, or de-energize the routine’s outputs on the mode transition.

  • Timers inside conditionally-called routines

    A TON in an unscanned routine stops accumulating but keeps its ACC and its DN state — when the routine is called again the timer resumes from where it froze, not from zero. If the pause should reset it, reset it explicitly on re-entry.

  • One-shot storage bits inside multi-call routines

    Call a routine twice per scan (two JSRs) and every ONS inside executes twice against the same storage bit — the second call sees the edge already consumed. Stateful instructions in shared routines need per-call data.

  • Recursion — a routine that JSRs itself (or its caller)

    PLCs fault on it (or hit the nesting limit). If two sections need each other’s results, restructure: compute in one routine, consume in a later one, and let the scan order be the sequencer.

  • Burying the only RES/unlatch for a tag in a rarely-called routine

    Cross-reference tools show where a tag is written, but not whether that routine is currently being scanned. When a latch "will not clear", check whether the clearing rung lives behind a false JSR.

Run JSR live — no install

Drop the instruction on a rung in the browser simulator, toggle the inputs, and watch the rung state, accumulator values and outputs update scan by scan.

Questions

JSR — frequently asked

What does the JSR instruction do in a PLC?

JSR (Jump to Subroutine) transfers execution to a named routine. The routine’s rungs execute top to bottom, then a RET (or the routine’s end) returns execution to the instruction following the JSR. If the JSR rung is true it happens every scan; if false, the routine is not scanned at all.

What happens to outputs in a subroutine that is not being scanned?

They freeze at their last written values. An unscanned routine is skipped entirely — its OTEs are not written false, its timers stop accumulating but keep their ACC and status bits. Any output that must drop when the routine stops running needs explicit cleanup logic in code that is still scanned.

What is the difference between JSR and JMP in ladder logic?

JSR calls a different routine and execution comes back (via RET) to continue after the call. JMP jumps forward to a LBL label within the same routine, skipping the rungs in between — nothing returns, and the skipped rungs behave like an unscanned zone. JSR structures programs; JMP short-circuits within one.

Can a JSR pass parameters to the subroutine?

In Studio 5000, yes: the JSR lists input parameters that are copied to the SBR instruction’s operands on entry, and the RET can pass return parameters back to the JSR. That lets one routine serve multiple stations. Siemens achieves the same with FC/FB parameter interfaces, and IEC 61131-3 with function/function-block arguments.