Omron PLC CMP Instruction: Running Multiple Compares in One Program
1. Overview: The Multi-CMP Problem in Omron PLCs
The CMP (Compare) instruction in Omron CS1, CJ1, CJ2, CP1, and CP-series PLCs is one of the most frequently used ladder instructions, yet it is also the source of one of the most common ambiguous-flag errors a maintenance engineer will encounter. Every time the CMP instruction executes, it overwrites the same three condition flags with the outcome of the comparison:
- P_GT – Greater than
- P_EQ – Equal to
- P_LT – Less than
Because those flags are shared, naively placing two CMP instructions back to back in the same scan will let the second CMP silently overwrite the first CMP’s result. By the time downstream logic evaluates the flags, they reflect only the last comparison executed, not the intended one. This is the classic ambiguous result flag failure mode that drives an entire station to behave erratically with no obvious logic error in the editor.
This reference documents the four engineering methods used to safely run multiple CMP blocks inside a single Omron PLC program: latching the result bits to internal relays, using a unique result word per CMP, switching to the CMP2 / symbol-form compare, and the Structured Text (ST) implementation path used in Sysmac Studio for NJ/NX controllers. Each method is paired with ladder samples, IO tables, and verification steps.
2. CMP Instruction Architecture: Result Flags and Execution Model
The CMP instruction lives in the CS/CJ/CP CPU instruction set as a single-output function that consumes two source words (S1 and S2) and an optional result word. The instruction is documented in the CJ2 CPU Unit Hardware User’s Manual (W472) and the CP1 CPU Unit Operation Manual (W394).
2.1 Instruction Format
| Operand | Meaning | Allowed Areas |
|---|---|---|
| S1 | First source word (16-bit signed/unsigned) | CIO, W, H, A, D, T/C, DM, @-indirect, *-indirect, constants (#) |
| S2 | Second source word | Same as S1 |
| C | Result word (optional in newer firmware) | W, H, A, D, CIO |
2.2 Condition Flag Behaviour
By default CMP writes its result into the shared arithmetic condition flags in the auxiliary area of the CJ2/CP1E/CP1H/CS1D family. The relevant bits and their default addresses are:
| Flag | Function | Shared Address (CJ2 example) | Symbolic Name |
|---|---|---|---|
| P_GT | Greater than | A200.05 | CMP_P_GT |
| P_EQ | Equal to | A200.06 | CMP_P_EQ |
| P_LT | Less than | A200.07 | CMP_P_LT |
On the CJ2M CPU units the result word operand (C) is mandatory on firmware 2.0+ for any CMP block that you want to write into a destination other than the auxiliary area. On CS1 and CJ1 units, the C operand was added in later firmware revisions; older units only write to the global flags.
2.3 Execution Timing
CMP is a single-scan instruction. It executes in one CPU cycle when its input condition is ON, writes the flags immediately, and is finished. There is no rising-edge detection built into the instruction. If the input condition is ON for ten consecutive scans, CMP runs ten times and writes the same flag pattern ten times – which means it will also overwrite a previously latched result from an earlier block during the same scan if the ladder order is wrong.
3. Why Multiple CMP Instructions Produce Ambiguous Results
Consider the following “obvious” ladder, which is the pattern that produces the most service calls:
|---[ CMP D0 D100 ]---( A200.05 )--> "high_speed_ok"
|
|---[ CMP D1 D100 ]---( A200.05 )--> "torque_ok"
Both rungs drive coil A200.05 (the P_GT bit). Whichever CMP block executes last in the scan determines the value of A200.05 for the rest of the cycle. Coils driven from A200.05 downstream therefore see only the last result, not the “AND” of both. The behaviour is rarely what the original programmer intended.
3.1 The Three Failure Modes
- Last-write-wins — The final CMP block in the scan overwrites every previous result.
- Cross-contamination — A GT flag from CMP-A is misread as the result of CMP-B by downstream logic.
- Rung-disabled results — If a CMP input condition is OFF, the flags are not cleared; they retain the previous scan’s values. This is documented behaviour, not a bug, but it is a common reason a “stuck” comparison flag appears after a mode change.
4. Method 1 — Latch CMP Result Bits with Internal Coils
The most common fix in legacy CS1/CJ1 code, and still the recommended pattern for technicians, is to snapshot the P_GT / P_EQ / P_LT flags into a private work-word immediately after each CMP, using a differentiated contact or a one-scan pulse to capture the result.
4.1 Ladder Example — Two CMPs, Latched to W0
; --- CMP #1: high-speed overspeed check ---
|---[ W0.10 ]---[ CMP D0 D100 ]---------------------|
| S1 S2 (implicit flags) |
| |
| +--[ A200.05 ]--[ DIFU 200.00 ]---( W1.00 )--+ | ; GT snapshot
| | | |
| +--[ A200.06 ]--[ DIFU 200.01 ]---( W1.01 )--+ | ; EQ snapshot
| | | |
| +--[ A200.07 ]--[ DIFU 200.02 ]---( W1.02 )--+ | ; LT snapshot
| |
; --- CMP #2: torque envelope check ---
|---[ W0.11 ]---[ CMP D1 D200 ]---------------------|
| |
| +--[ A200.05 ]--[ DIFU 200.03 ]---( W1.10 )--+ | ; GT snapshot
| | | |
| +--[ A200.06 ]--[ DIFU 200.04 ]---( W1.11 )--+ | ; EQ snapshot
| | | |
| +--[ A200.07 ]--[ DIFU 200.05 ]---( W1.12 )--+ | ; LT snapshot
4.2 Why DIFU Is Required
DIFU (Differentiate Up) converts the momentary ON of a flag into a one-scan pulse, which the trailing coil uses to set a sticky bit. Without DIFU, the result coil would track the raw flag bit by bit, so you would still be subject to last-write-wins on the same scan. With DIFU, the snapshot coil latches after CMP has finished, isolating the two comparisons from each other.
4.3 Memory Map for the Latch Words
| Word | Bit | Meaning | Use in downstream logic |
|---|---|---|---|
| W1.00 | CMP1_GT | Speed > limit | Trip overspeed alarm |
| W1.01 | CMP1_EQ | Speed = limit | Target band reached |
| W1.02 | CMP1_LT | Speed < limit | Spindle accel permissive |
| W1.10 | CMP2_GT | Torque > envelope | Mechanical jam |
| W1.11 | CMP2_EQ | Torque = envelope | Steady state |
| W1.12 | CMP2_LT | Torque < envelope | Light load |
4.4 Reset Path
Because the latched bits are now “sticky,” add a master reset rung to clear W1.00–W1.12 on mode change or cycle end:
|---[ W0.00 ]---( RSET W1 )--| ; All 16 bits of W1 cleared on one-shot reset
5. Method 2 — Use a Unique Result Word per CMP
On CJ2M firmware 2.0+ and CS1D, CMP accepts an explicit result word operand. The instruction then writes its result into a private word instead of the shared auxiliary area, which means two CMP blocks can run in the same scan without interfering with each other.
5.1 Ladder Example — Unique Result Word
|---[ W0.10 ]---[ CMP D0 D100 W2 ]---|
| S1 S2 C |
|
|---[ W0.11 ]---[ CMP D1 D200 W3 ]---|
| S1 S2 C |
5.2 Bit Decoding of the Result Word
| Result Word Bit | Meaning | For W2 (CMP1) | For W3 (CMP2) |
|---|---|---|---|
| Bit 15 (MSB) | Greater than | W2.15 | W3.15 |
| Bit 14 | Equal to | W2.14 | W3.14 |
| Bit 13 | Less than | W2.13 | W3.13 |
The same decoding pattern is documented in the CJ2 CPU Unit Software Manual (W473) for the BCMP2 and TCMP table-compare instructions, and applies to the legacy CMP C-operand form on every CJ2 firmware after v1.3.
5.3 Pros and Cons of Method 2
| Aspect | Method 2 (Unique Result Word) |
|---|---|
| Scan determinism | Excellent — no flag collision |
| CPU load | Lower (no DIFU, no extra rungs) |
| Firmware requirement | CJ2 v2.0+ / CS1D / CP1H v1.0+ |
| Readability | Highest — results are private per block |
| Migration cost from legacy CMP | Medium — must add the C operand |
6. Method 3 — Use CMP2 (Symbol Compare) and Table Compare
Omron added a number of compare variants in the late CS1 / CJ1 era that store their result in a private word by design. They are often the cleanest way to embed multiple comparisons in dense logic.
6.1 CMP2 — Symbolic Comparison
CMP2(2) is the unconditional compare. It accepts the same S1, S2 operands as CMP but always writes its result into a private word passed as a third operand. It does not touch the global P_GT/P_EQ/P_LT flags.
|---[ W0.10 ]---[ CMP2(2) D0 D100 W2 ]---|
| S1 S2 C |
6.2 Comparison Family and Result Word Layout
| Instruction | Mnemonic | Result Word Bits | Notes |
|---|---|---|---|
| Compare | CMP / CMP2 | 15 = GT, 14 = EQ, 13 = LT | 16-bit signed or unsigned |
| Double Compare | CMPL / CMPL2 | 15 = GT, 14 = EQ, 13 = LT | 32-bit, S1+1 / S2+1 |
| Table Compare | TCMP / TCMP2 | 16 bits, one per table row | 16-row table, C is mask |
| Block Compare | BCMP / BCMP2 | 16 bits, one per range | Upper/lower table |
| Zone Compare | ZCP / ZCP2 | 15 = GT, 14 = in range, 13 = LT | Tests if S1 is between LL and UL |
6.3 Why CMP2 Is Preferred for Multi-Compare Programs
- No global flag pollution — Other parts of the program reading P_GT / P_EQ / P_LT are isolated.
- One mnemonic, one result word — Ladder is self-documenting; the trailing C operand documents which work word owns this comparison.
- Compatible with function blocks — In a structured FB, each instance can be passed its own C operand, which scales to dozens of comparisons without symbol conflicts.
7. Method 4 — Sysmac Studio Structured Text (NJ/NX)
On the NJ and NX series controllers programmed with Sysmac Studio, the legacy CMP instruction is replaced by the EQ, GT, LT, GE, LE, NE operators inside Structured Text. There is no shared flag area in the same sense, so multiple comparisons in the same POU are inherently safe. However, the engineering discipline of isolating each comparison into its own boolean variable is still required for clean code review and FDA-style validation.
7.1 ST Example — Multiple Compares in One POU
// Conveyor and spindle comparison block
IF bCompareEnable THEN
bSpeedHigh := (iActualSpeed > iSpeedLimit); // GT -> bSpeedHigh
bSpeedOK := (iActualSpeed = iSpeedLimit); // EQ -> bSpeedOK
bSpeedLow := (iActualSpeed < iSpeedLimit); // LT -> bSpeedLow
bTorqueJam := (iActualTorque > iTorqueEnvelope);
bTorqueOK := (iActualTorque = iTorqueEnvelope);
bTorqueLow := (iActualTorque < iTorqueEnvelope);
END_IF;
7.2 Mixing Ladder and ST in Sysmac
Many engineers keep a ladder routine for the IO scan and use a single ST routine named RPM_TorqueLogic to host all the comparisons. This makes code review trivial — one file, one set of boolean outputs, no shared global flag area to audit.
7.3 Ladder Variant in Sysmac
If you need ladder for shop-floor technicians, the equivalent is the >, =, < contact instructions. Each is a stateless block that does not pollute any global flag area:
|---[ D0 > D100 ]---( bSpeedHigh )---|
|---[ D0 = D100 ]---( bSpeedOK )---|
|---[ D0 < D100 ]---( bSpeedLow )---|
8. Step-by-Step Implementation Procedure
- Identify the CPU model and firmware. In CX-Programmer, double-click the CPU entry in the project tree. CJ2M units below firmware v1.3 cannot use the C operand on CMP and must use Method 1 (latch with DIFU). CJ2M v2.0+ can use Method 2 (unique result word).
- Define a private work word per logical comparison. Allocate W2, W3, W4, … in the symbol table. Document each bit (GT, EQ, LT) in the symbol comments.
- Replace CMP with CMP2, or add the C operand to CMP, on every comparison that previously relied on the global flags.
-
Map every consumer of P_GT / P_EQ / P_LT to the new private word, then search the program for any remaining
A200.05,A200.06,A200.07references to confirm they are intentional. - Add a global reset rung for the latched work words. Tie it to a mode-change flag or cycle-complete flag to avoid stale comparisons across batches.
- Verify the symbol export for any HMI or SCADA tag that previously bound to the global P_GT. Update the OPC tag to point to the new private word.
- Run a structured offline simulation in CX-Programmer. Force each comparison source to a known value, step the program, and confirm that only the intended result word changes.
9. Verification and Debugging Techniques
After the conversion, validate that the ambiguous-flag failure is gone. The following checks are field-proven and work on every Omron PLC in the CS/CJ/CP/NJ family.
9.1 Watch Window Checklist
| Item | What to monitor | Expected value |
|---|---|---|
| W2.15 / W2.14 / W2.13 | CMP1 result bits | Exactly one bit ON at a time |
| W3.15 / W3.14 / W3.13 | CMP2 result bits | Exactly one bit ON at a time |
| A200.05–07 | Global flags | Should be stable or intentionally driven |
| W1.00–W1.12 | Latched snapshot bits (Method 1) | Stable across scans; only change on edge |
9.2 Data Trace Setup
Set up a 1-second data trace on the comparison sources, the result words, and the global flags. The trace should be long enough to cover one full mode change (typically 5–30 s). If you ever see A200.05 toggle when only CMP2 should be running, the global flag is still being touched and the migration is incomplete.
9.3 HMI Cross-Check
If the SCADA was bound to the global P_GT bit, force the bit in CX-Programmer and confirm the HMI shows the right alarm. Repeat for the private result word. If the HMI still reads A200.05, it will keep showing the old (last-write-wins) result until the tag is updated.
9.4 Scan Time Impact
CMP2 adds no measurable scan time compared to CMP on CJ2M and CP1H. Adding DIFU per CMP block adds roughly 3–5 µs per block on a CJ2M-CPU31, which is negligible. The bottleneck in nearly every case is the read of the comparison sources, not the instruction itself.
10. Common Errors and Resolution Matrix
| Symptom | Likely Root Cause | Fix |
|---|---|---|
| Comparison result flickers ON/OFF every scan | Two CMP blocks share P_GT; flag is overwritten | Apply Method 2 (C operand) or Method 3 (CMP2) |
| Result stays ON even after source changes | DIFU is missing; the snapshot coil tracks the raw flag | Insert DIFU between flag and latching coil |
| Result never updates | Result word assigned to a read-only area (e.g., EM bank protected by UM) | Reassign to a writable work area in CIO/W/D |
| PLC online shows 0x0000 in result word | CMP not actually executing (input condition OFF) | Verify input rung; CMP does not clear stale flags |
| HMI alarm lags by one cycle | HMI tag still bound to global flag, not the private word | Rebind HMI tag to W2.15 / W3.15 / etc. |
| “Operand type error” on transfer of CMP | CJ2M unit below firmware v1.3 cannot accept C operand | Use Method 1 (DIFU latch) or upgrade firmware |
| CMP result wrong on first scan after power-up | Flags retain power-down state; no first-scan reset | Add P_First_Cycle (A200.11) reset of all comparison result words |
| Two CMPs run “in parallel” but logic behaves sequentially | Method 1 used without DIFU; coil races between the two blocks | Insert DIFU on each flag path; re-test |
11. Best Practices and Field-Proven Patterns
- Never read global P_GT / P_EQ / P_LT directly from more than one comparison block. If you must, use the global flags only for a single dedicated CMP that is reserved as the “system comparison.”
- Reserve W2–W31 (or W100–W131) for comparison result words. Document the allocation in the symbol table so future engineers do not reuse a comparison result as a scratch word.
- Use P_First_Cycle (A200.11) to clear all comparison result words on power-up. Stale comparisons at first scan are a common source of nuisance trips.
- Prefer CMP2 over CMP on new code. The only reason to use the global-flag CMP is for backward compatibility with an existing routine that reads the global flag.
- Document every comparison with a one-line comment naming the source, the limit, and the consumer. A five-line program may not need it; a 200-rung program definitely does.
- For high-speed comparisons on the same two words, use the <, =, > contact instructions. They execute in a single bit slice and avoid the auxiliary flag area entirely.
- On NJ/NX projects, default to Structured Text for the comparison block, but keep one ladder routine for the IO scan so shop-floor technicians can troubleshoot on the console.
- When validating per FDA 21 CFR Part 11 or GAMP 5, capture the symbol table snapshot in the validation report. A change in the comparison result word allocation must trigger a regression review.
12. Frequently Asked Questions
Can I use the CMP instruction more than once in the same Omron PLC program?
Yes, the CMP instruction can appear multiple times in the same program, but each CMP block writes to the same shared auxiliary flags (P_GT at A200.05, P_EQ at A200.06, P_LT at A200.07). To prevent the last CMP block from overwriting earlier results in the same scan, add the optional C operand to write into a private result word, replace CMP with CMP2, or latch each flag to its own internal coil using DIFU before any other CMP runs.
Does the Omron CMP instruction clear the result flags when the input condition is OFF?
No. The auxiliary area condition flags are not cleared when CMP is skipped. If the input condition turns OFF, the previous result remains in the flags until the next CMP execution. The recommended pattern is to snapshot each flag into a sticky work bit via DIFU and clear the work bit on a mode change or first-scan flag, not to rely on the raw flag bit.
What is the difference between CMP and CMP2 in Omron PLCs?
CMP writes its result into the shared auxiliary flags A200.05–A200.07, while CMP2 always writes its result into the private result word supplied as the third operand and does not touch the global flags. CMP2 is the safer choice when multiple comparisons live in the same program because each instance owns its own result word, eliminating last-write-wins errors.
On which Omron CPUs can the CMP instruction use a result word (C operand)?
The C operand is supported on CJ2M firmware v2.0 and later, on CS1D, and on CP1H / CP1L CPUs that include the relevant instruction expansion. Older CJ1M and CS1G units without the firmware update cannot accept the C operand; on those, use the DIFU latch method or replace CMP with CMP2. Always confirm support in the CPU unit’s operation manual before relying on the C operand in production code.
How do I run multiple comparisons in an NJ or NX series controller?
Use the EQ, GT, LT, GE, LE, and NE operators in Structured Text inside Sysmac Studio, or the <, =, > contact instructions in ladder. The NJ/NX architecture does not use the legacy auxiliary flag area, so multiple comparisons in the same POU are inherently isolated. For shop-floor readability, keep all comparisons in one ST routine named after the function (for example RPM_TorqueLogic) and assign each result to its own named boolean variable.