Configuring Productivity 3000 Tasks to Replace DL Stages

Brian Holt14 min read
AutomationDirectHMI ProgrammingTechnical Reference
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

The usual first attempt at porting a DirectLOGIC RLL Plus stage program to a Productivity 3000 is to turn each stage into a called task and drive each CALL TASK from the old S-bit. It downloads, the sequence steps, and then a lamp keeps flashing or a valve stays energized after the machine has moved on. The cause is a difference in how the two platforms treat logic that is no longer active. Stages turn off their OUT coils when they deactivate. Called tasks do not. The sections below rebuild the program in order. Each section ends with a check, and you pass that check before you move to the next section.

Stop Mapping Stages One-for-One to Called Tasks

First you need to know what a stage actually does, because the fix depends on it. The DL205 User Manual (stage programming chapter) says a stage with its bit at 0 is "not scanned" and a stage with its bit at 1 is scanned. That wording is misleading. The stage bit acts like a master line set (MLS) on that block of rungs. When the stage is inactive, its rungs are still evaluated, but with the power rail false. Every rung in the block therefore evaluates false.

The effect of a false rung depends on the instruction. Some DirectLOGIC instructions do something when false, and others do nothing:

STR X1       ; IF X1 == 1
OUT Y1       ; THEN Y1 = 1  ELSE Y1 = 0     <-- acts on false

STR X1       ; IF X1 == 1
SET Y1       ; THEN Y1 = 1  ELSE do nothing

STRN X1      ; IF X1 != 1
RST Y1       ; THEN Y1 = 0  ELSE do nothing

STR SP1      ; IF SP1 == 1
LD V2000     ; THEN load V2000 to accumulator  ELSE do nothing

When a stage is turned off by JMP or RST, the CPU scans it one last time with the rail false. That final scan drops every OUT coil in the stage and resets non-retentive timers whose enable is now false. Stage programmers rely on this without thinking about it, and it is why stage programs need so few one-shots and blocking bits.

A Productivity 3000 called task behaves like a subroutine. CALL TASK jumps execution into the called task immediately. When the task's END is reached, execution returns to the rung after the CALL TASK. When the call is not enabled, the task's rungs are not executed at all. They are not evaluated false. Their outputs keep whatever value they had on the last scan in which the task ran. Subroutines on Allen-Bradley controllers and on the DL line behave the same way.

Behavior DL RLL Plus stage P3000 called task
Logic when inactive Rungs evaluated with power rail false Rungs not executed
OUT coil when block goes inactive Turned off (final scan) Holds last state
Flashing output stopped mid-cycle Goes off Stays on if it was on
SET/RST latches Retained Retained
Non-retentive timer when block goes inactive Enable false, timer resets Not executed, value frozen
Repeat execution Once per scan while active Can be called more than once per scan
Nesting N/A (flat stages) A called task could not call another called task in early releases

Decide before you commit. If the machine relies heavily on stage sequencing and the team is fluent in RLL Plus, staying on a DL CPU is a legitimate choice. If you move to the P3000, you gain tag-based addressing, typed data, copy/paste, Ethernet drive and remote I/O setup, and a large set of instructions (linear and non-linear scaling, flash, debounce, and an OUT that becomes a one-shot with a checkbox). You pay for those with the extra logic described below.

Check: you can explain, for your own program, which stage deactivations currently turn something off without any explicit rung doing it. If you can't, do the inventory in the next section before writing any P3000 logic.

Inventory Every Stage Output, Timer, and Latch

List every hidden turn-off before you write anything. A stage program does much of its turning off implicitly, and every implicit turn-off becomes a rung you have to write.

  1. Print the stage program by stage.
  2. For each stage, list every OUT coil. Mark whether the same output is also written in another stage.
  3. List every timer and counter inside a stage. Mark which ones must restart from zero each time the stage is entered.
  4. List every SET/RST. These already behave identically on both platforms and need no special handling.
  5. List every JMP and stage RST, including abort and fault paths. These become the state transitions.
  6. Note the one-shots and interlock bits that were never needed because of stage behavior. Called tasks will need some of them back.

Check: every OUT coil and every timer in the stage program appears on the list with an owning stage. Stop here if any output is written by OUT in more than one stage. That double-coil pattern works in stages because only the active stage's rung is true. Ported directly into tasks, it becomes a fight between frozen values. Resolve ownership on paper first.

Configure the Hardware Tree and Rename the I/O Tags

Hardware configuration is on the right side of the programming window. Drag the base and each module into the tree. You can also let the software read the installed hardware automatically, but compare the result against the physical rack before you trust it. Click each module to fill in its settings.

  • The default I/O tags follow the module position, for example DI.1.1.1. Overwrite them with functional names such as ioStartPB and ioStopPB.
  • Analog modules accept different signal types on the same module. Set each channel to its field device instead of assuming the whole module is 4-20 mA.
  • Discrete modules need more wiring than DL modules. DL modules shared one or two commons for power and ground. Check the P3000 module wiring diagram per point group before you pull wire from the old panel.
  • Terminal covers and connectors ship separately, because you can use a screw-terminal connector or a ZIPLink pre-wired cable. Order the one that matches your panel design.

Check: with the CPU online, toggle each discrete input and watch its renamed tag change. On analog modules, use the module's built-in display, which shows the channel value in volts or milliamps, decimal, or hex. Compare it against a calibrator at 0%, 50%, and 100%. Do not start logic until every point reads correctly.

Name Tags With a Type Prefix Before Writing Logic

There is no V-memory to reason about any more. On a DL CPU, the format of a V-register is decided by the instruction that reads it (ADD treats it as BCD, ADDB as binary), not by the register. The P3000 attaches a data type to each tag. That removes the number-format mistakes, but it moves the burden to the tag name.

The tag name is the only description you get. DirectSOFT gave you an address, nickname, description, and wiring info. The P3000 gives you the tag name. An instruction comment appears only on the instruction it is attached to, not everywhere the tag is used. When you later connect a C-more HMI, the tag name is all you see, and nothing there tells you whether a tag is a single or double integer.

Prefix (example convention) Use
io Physical I/O points
st State bits replacing S-bits
di Double integer values
tmr / cnt Timer and counter structures

Pick your own prefixes and hold to them. Early releases of the software had no search-and-replace. Renaming tags halfway through a project was expensive, so settle names now.

Check: review the tag list. Every tag carries a prefix that matches its type and its role. Every former S-bit has a matching st Boolean.

Build the State Bits in a Run-Every-Scan Main Task

Put only one task in the Run Every Scan group, called something like Main. This task owns the sequence. It holds the state bits, the transitions, and, from the section on outputs, the physical outputs. Every other task is a called task.

Write each former JMP as a transition that sets the next state and clears the current one in the same rung. This reproduces the stage rule that only one step in a sequence is active.

// Main task (Run Every Scan) - example tag names
// Transition: Fill -> Mix
| stFill  ioLevelHigh |-----(SET stMix)
|                     |-----(RST stFill)

// Transition: any state -> Idle on stop or fault
| ioStopPB |------------(RST stFill)
|          |------------(RST stMix)
|          |------------(RST stDrain)
|          |------------(SET stIdle)

The abort rung matters more here than in a stage program. Under stages, resetting a stage cleans up its outputs automatically. Under tasks, resetting a state bit only stops the call. Nothing else happens by itself.

Check: in a data view, drive the sequence with inputs or forces through every transition, including stop and fault from every state. Exactly one state bit in the sequence is true at every step. If two are ever true together, fix it now.

Call One Task per Former Stage

Below the transitions in Main, enable one CALL TASK per state from its state bit:

// Main task, after transitions
| stFill  |----[CALL TASK  Fill]
| stMix   |----[CALL TASK  Mix]
| stDrain |----[CALL TASK  Drain]

In Main you now see only the calls, not every step's logic in one long rung list, and that gives you most of the readability of stages. Put the state-specific calculations, permissives, and internal bits inside each called task. Do not put physical outputs in called tasks. The next section explains why.

Watch the execution order. CALL TASK runs the task at that point in the scan and returns to the next rung. A transition written above the calls takes effect on the same scan. A transition written inside a called task takes effect on the next pass through Main. Pick one location for all transitions. Keeping them all in Main is easier to troubleshoot at night.

Check: put a scratch counter at the top of each called task. Step through the sequence. Only the counter for the active state increments, and every counter stops the moment its state bit drops.

Drive Physical Outputs From the Main Task, Not the Called Tasks

This is where a straight port fails in production. For example, a flash instruction inside a called task drives a beacon. If the state bit drops while the beacon is on, the beacon stays on. The task that would turn it off is no longer executed.

These quick fixes do not work:

  • Putting a turn-off rung at the bottom of the called task. It never runs, because the task stops being called before it can execute.
  • Calling the task one extra scan after the state drops. This recreates the stage's final scan by hand. You need an extra bit and a one-shot per state, and every rung in the task must evaluate false on that pass. You end up with more logic than the pattern below.
  • Replacing OUT with SET/RST inside the tasks. Latches hold on both platforms. This only moves the problem into your abort paths.

What restores production: write every physical output in Main, once, as an OR of the states that own it, ANDed with its permissives.

// Main task - one rung per physical output
| stFill |--+--| NOT ioFault |----(OUT ioFillValve)
| stTopUp|--+

| stMix  |-----[FLASH ... ]------(OUT ioBeacon)

When a state drops, the OR goes false on the same scan and the OUT turns the output off. That is the stage behavior, now written explicitly. Called tasks still do the state's work. They write internal request bits (for example stMixSpeedReq), and Main decides what reaches the output card.

If moving outputs is impossible, because the code is too large to rework on shift, use the fallback. Add explicit RST instructions for each output the old state owned into the transition rung that leaves that state. It works, but every new output means editing every exit path. Get it running this way, then move the outputs to Main properly.

Check: for every output on your inventory, energize it in its owning state, then force or trigger a transition out of that state. The output must drop on the next scan. Repeat with the stop/fault path from each state. Test flashing outputs at least ten times so you catch the "on" half of the cycle.

Reset Timers and Counters on State Entry

A non-retentive timer inside a DL stage resets when the stage goes inactive, because its enable goes false. A timer inside a P3000 called task is simply not executed, so its accumulated value stays where it was. On the next entry to that state, the timer may resume partway through or report done immediately. The sequence then skips a step, and nobody sees why.

Pick one of these fixes per timer:

  1. Move the timer to Main and enable it with the state bit, plus any condition from the task. It resets whenever the state drops, which matches stage behavior.
  2. Reset on entry. In the transition rung that enters the state, reset the timer or counter. The OUT one-shot checkbox gives you a clean entry pulse if you need one.

Apply the same rule to counters that must restart each cycle and to any accumulating math such as totals, averages, or step indexes.

Check: enter each timed state, abort halfway, re-enter. The timer must start from zero. Run each timed state twice in a row through the full cycle and compare elapsed times.

Flatten Nested Calls and Clear the Default END Rungs

Two editor behaviors cause trouble after the logic looks correct.

Nested calls. Early P3000 releases did not allow a called task to call another called task. A stage program ported with sub-sequences inside steps runs into this at once. Keep the structure flat: every CALL TASK in Main, with sub-steps as additional state bits. Check the CALL TASK help topic in your installed software version for the current rule. Do not design around nesting until you have confirmed it there.

END rungs. When you create a task, the editor fills rungs with END by default. If you skip rungs to leave space while writing, the END left in a skipped rung stops execution of that task at that point. Any logic below it is silently dead. To get blank separator rungs, insert new rungs or change the ENDs to NOP.

Check: put a scratch coil on the last rung of every task and confirm it energizes online. Any task where it stays off has a stray END above it.

Run the End-to-End Verification

Complete these in order. The software supports downloading with the CPU running, and you can monitor online as you did in DirectSOFT, so fix and retest as you go.

  1. Dry cycle with outputs isolated. Step the full sequence. Confirm one active state at a time and the correct output tags per state.
  2. Abort from every state. Trigger stop and each fault in every state. Every output owned by that state drops, and every timer returns to zero on re-entry.
  3. Power cycle mid-sequence. Confirm the startup state and that no output comes back on from a retained latch.
  4. Networked devices. If GS-series drives and remote I/O are on Ethernet, confirm each one follows its state and goes to its safe condition when the owning state drops. Pull the network cable on one device and confirm the fault path runs.
  5. HMI and SCADA tags. Confirm C-more tag types match the P3000 tag types, using the prefixes from the tag naming section. For OPC, any Modbus-capable OPC server can talk to the P3000, and Kepware offers a P3000 driver. Read the state bits and key values from the SCADA side and compare them against online monitoring.
  6. Live run under supervision. Run full production cycles and watch each flashing, timed, and shared output through at least one abort.

FAQ

How do I make Productivity 3000 outputs turn off when a called task stops running?

Move each physical OUT into the Run Every Scan main task and drive it from the OR of the state bits that own it. A called task that is no longer called is not executed, so its outputs hold their last value. The only in-task alternative is explicit RST of those outputs in every transition that leaves the state.

How do I reset a timer inside a Productivity 3000 called task like a DirectLOGIC stage did?

Either move the timer to the main task and enable it with the state bit, or reset it in the transition rung that enters the state. A timer in an uncalled task freezes at its accumulated value instead of resetting.

Can a Productivity 3000 called task call another called task?

Early releases did not allow it, so build a flat structure with every CALL TASK in the main task and sub-steps as extra state bits. Check the CALL TASK help topic in your installed version before relying on nesting.

Stop and contact AutomationDirect technical support if an output stays on after its state bit is false and no rung in Main or any called task writes it. Also contact them if CALL TASK behaves differently from its help topic in your software version. Send the project file together with the software and CPU firmware versions read from the controller.

Back to blog