Resolving Siemens S7-300 BCD Conversion Error 16#2521 and SF LED

David Krause18 min read
S7-300SiemensTroubleshooting
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

Resolving Siemens S7-300 BCD Conversion Error 16#2521 and SF LED

The CPU 315-2DP from the SIMATIC S7-300 family (typical article numbers 6ES7315-2AG10-0AB0, 6ES7315-2EH13-0AB0, 6ES7315-2AF03-0AB0) raises event ID 16#2521 in the diagnostic buffer whenever an instruction tries to convert a value into or out of Binary-Coded Decimal (BCD) and the operand cannot be represented. The visible symptom is a latched SF (System Fault) LED that returns after every power-up and every CPU restart. The diagnostic buffer entry typically shows:

  • Event ID: 16#2521 (BCD conversion error)
  • OB: OB121 (Programming error organization block)
  • Priority class: 1
  • Affected register: Accumulator 1
  • Direction: Incoming event (with a matching outgoing event on acknowledgement)

Even when OB121 is loaded so that the CPU stays in RUN, the SF LED remains illuminated and the buffer is appended every time the offending instruction executes. Resolving the fault requires locating the exact network, fixing the value being converted (or the instruction that references it), and only then clearing the diagnostic buffer and the latched LED.

This reference covers the encoding of BCD and S5TIME values, the precise trigger conditions for 16#2521, the STEP 7 V5.x diagnostic workflow, firmware-specific edge cases on older S7-300 CPUs, and the verification steps needed to confirm the SF LED has cleared. For the underlying BCD encoding theory, see the Binary-coded decimal reference. The official Siemens product documentation set is available from the SIMATIC S7-300 manuals and firmware portal.

1. BCD and S5TIME Encoding Reference

Event 16#2521 fires when the value in accumulator 1 cannot be encoded as BCD or contains an invalid BCD nibble. A working mental model of the BCD format is required to localise the fault.

1.1 16-bit BCD word (result of ITB / input to BTI)

Bit 15 14 13 12 11-8 7-4 3-0
Content Sign nibble (S) Hundreds (BCD) Tens (BCD) Ones (BCD)

Bits 12-15 form the sign nibble. 0000 represents positive, 1111 represents negative. The remaining 12 bits (bits 0-11) hold three BCD digits, each 0-9. The valid value range is -999 to +999. The sign occupies the upper nibble, leaving only three decimal digits - this is why the ITB integer-to-BCD limit is 999, not 9999 as the bit count might suggest.

1.2 32-bit BCD double word (result of DTB / input to BTD)

Bit 31-28 27-24 23-20 19-16 15-12 11-8 7-4 3-0
Content Sign nibble (S) Seven BCD digits, 0-9 each (millions down to ones)

Valid DTB range is -9,999,999 to +9,999,999. The sign pattern in the upper nibble is the same convention as the 16-bit BCD word.

1.3 S5TIME format (legacy S5 timer preset)

Bit 15-14 13-12 11-8 7-4 3-0
Content Reserved (must be 0) Time base Hundreds (BCD) Tens (BCD) Ones (BCD)

Time base encoding:

  • 00 = 0.01 second (10 ms)
  • 01 = 0.1 second (100 ms)
  • 10 = 1 second
  • 11 = 10 seconds

The BCD value occupies bits 0-11 (max 999). Maximum time value is therefore 999 × 10 s = 9990 s. The reserved bits (15-14) must be zero. Any deviation triggers 16#2521 on timer load instructions (SP, SE, SD, SS, SF, FR).

1.4 S7TIME / IEC TIMER format (TP, TON, TOF, TONR)

The IEC timers in STEP 7 V5.3 and later store the time as a 32-bit DINT count in milliseconds, not as BCD. The S7TIME type is declared as a length × time-base combination (e.g., S5TIME#2s200ms) and converted internally. Migrating S5TIME-based timers to S7TIME eliminates BCD conversion on timer load and avoids the 16#2521 trigger condition from S5 timer range overflows. The IEC timer blocks are SFB3 (TP), SFB4 (TON), SFB5 (TOF), and SFB103 (TONR), found in the Standard Library > Timers > SFB folder of the STEP 7 V5.x installation.

1.5 S7 C0-C255 counter preset

When a counter preset word is loaded via L C# or assembled via L W#16#..., the value is stored in BCD with bits 0-15 representing up to three BCD digits plus a sign pattern. Maximum counter value is 999. Loading a word that contains hex values 1010-1111 (A-F) in any nibble triggers 16#2521.

2. Root Cause: Conditions That Raise 16#2521

The S7-300 firmware raises 16#2521 in five distinct situations. Each is verifiable with a single rule.

BCD Conversion Fault - Decision Tree Instruction executes Opcode: ITB / DTB? Yes No ITB: |ACCU1| > 999?-> 16#2521 DTB: |ACCU1| > 9999999?-> 16#2521 Opcode: BTI / BTD / S5 timer load / S7 counter preset? Any nibble contains 1010b to 1111b (A-F)?-> 16#2521

2.1 ITB overflow (Integer to BCD)

ACCU1-L contains a 16-bit INT. The valid range after sign extraction is -999 to +999. The condition is:

|ACCU1-L| > 999 → 16#2521

Common offenders: an analog input scaled into 0-10000 engineering units converted with ITB to drive a 4-digit BCD display, or a counter running unchecked past 999 then converted. The 1000 boundary is the most reported: a value of 999 encodes as 16#0999 (3 decimal digits plus positive sign), but 1000 would require a fourth decimal digit which the format does not provide, so the converter rejects the value.

2.2 DTB overflow (Double Integer to BCD)

ACCU1 contains a 32-bit DINT. Valid range after sign extraction is -9,999,999 to +9,999,999. The condition is:

|ACCU1| > 9,999,999 → 16#2521

2.3 BTI / BTD pseudotetrade

When converting BCD to integer, each nibble must be 0-9. Any nibble equal to 10-15 (hex A-F) is called a pseudotetrade and triggers 16#2521. This typically occurs when a binary (INT/DINT) value is loaded into a BCD-typed tag and the conversion is later attempted. The test is a bitwise AND of the input with the pseudotetrade mask:

(input AND 16#F000) > 16#9000 for a 16-bit word (test the upper nibble); repeat for each nibble.

2.4 S5TIME / S7TIME format violation

Loading a value into a legacy S5 timer instruction (SP, SE, SD, SS, SF) with bits 0-11 containing hex A-F, or with bits 15-14 non-zero, or with bits 13-12 outside the two-bit range, triggers 16#2521. The S7TIME conversion performed internally for IEC timers does not use BCD on the value field and is therefore not subject to this constraint.

2.5 S7 counter preset violation

The preset for S7 counters (C0-C255) must be valid BCD in the range 0-999. The same pseudotetrade condition applies as for BTI.

3. Locating the Faulty Instruction in STEP 7 V5.x

On S7-400, the programming error OB121 startup information includes the block type, block number, and the relative address of the instruction that triggered the error. The STEP 7 stack displays the exact network. On S7-300, OB121 startup information is more limited and frequently only the accumulator content is preserved. The diagnostic workflow is therefore:

  1. Open SIMATIC Manager and establish an online connection to the CPU (PLC > Connect to Target System or via the Accessible Nodes browser).
  2. Right-click the CPU in the project tree and choose PLC > Module Information (or use the diagnostic icon in the toolbar).
  3. Select the Diagnostic Buffer tab.
  4. Locate the 16#2521 entry. The top of the diagnostic list shows the most recent events. Open the entry to see the time, the affected OB (OB121), and the accumulator snapshot.
  5. Click Open Block at the bottom of the dialog. STEP 7 opens the block in the offline program and highlights the network containing the offending instruction, provided the offline program matches the online content. If the offline project is out of sync, use the cross-reference.
  6. If Open Block is greyed out, manually scan the program. Use Options > Cross-Reference (Ctrl+Shift+F) and search for the data address (MW, DBW, or specific tag) that is referenced in the suspect code.

The navigation through Module Information is also accessible via the online port of the S7-PLCSIM simulator when testing offline. The same path applies to S7-PLCSIM V5.4 SP5 and later, allowing the 16#2521 event to be reproduced in a controlled environment before downloading changes to the real CPU.

Important: On S7-300 with firmware versions older than V2.8.x (CPU 31x-2DP part numbers ending -0AB0 and -0AB1), the OB121 startup information does not always include the relative instruction address. If the diagnostic buffer is empty for 16#2521 apart from the accumulator snapshot, instrument the suspect blocks with breakpoint instructions (STP) or set up online monitoring of all data words used by the ITB/BTI chain. STP forces the CPU into STOP with a recorded breakpoint, which allows narrow isolation of the triggering scan.

4. Resolving ITB and DTB Overflow

The recommended pattern is to validate the input range before issuing the conversion, then clamp the value to the BCD-encodable maximum. For ITB, the maximum is 999. For DTB, the maximum is 9,999,999. The pattern is also valid for scaled analog values: scale the engineering units to a range that fits the BCD format from the project specification, and apply the clamp at the boundary.

4.1 STL implementation (ITB overflow guard)

// Source: scaled analog input in MW100, 0-27648 raw
// Target: BCD display value 0-999 in MW200
      L     MW100                // load raw value
      L     999                  // limit
      <I                          // ACCU2-L < ACCU1-L?
      JC    CLAMP                // jump if raw > 999
      L     MW100                // re-load raw
      JU    CONVERT
CLAMP: L     999                  // replace with max
CONVERT:ITB                      // ACCU1-L: INT -> BCD
      T     MW200                // store as BCD for display

For the dual-direction case where the value can be negative, the same pattern using ABS and a separate sign test is required:

      L     MW100                // signed INT
      L     0
      <I                          // ACCU2 < 0?
      JC    NEG
      L     999
      <I                          // positive overflow?
      JC    CLAMPP
      L     MW100
      JU    POSBCD
NEG:   L     -999
      >I                          // negative overflow?
      JC    CLAMPN
      L     MW100
      JU    POSBCD
CLAMPP:L     999
      JU    POSBCD
CLAMPN:L     -999
POSBCD:NEGI                      // make positive if negative
      ITB                         // ACCU1-L -> BCD
      T     MW200                 // store (sign preserved in bit 15)

4.2 LAD implementation (ITB overflow guard)

[ MW100 ] --[ CMP > 999 ]--+--[ MOVE 999 to MW102 ]--
                            +--[ MOVE MW100 to MW102 ]--
[ MW102 ] --[ ITB ]----------[ T MW200 ]

For DTB the equivalent limit is 9,999,999. Use the same pattern with MD (double-word) data tags. When the source is a real (32-bit floating point) value, convert with ROUND or TRUNC to DINT first, then clamp, then DTB.

5. Resolving BTI and BTD Pseudotetrade Errors

When converting a BCD word to integer, mask and test each nibble for values A-F. If any nibble is invalid, replace the value with a defined default (0 or 9999) before conversion. The pseudotetrade test is the only safe way to handle tag data of unknown origin, especially when the value is read from a partner CPU, an HMI panel, or a third-party device over PROFIBUS DP.

5.1 STL implementation (BTI pseudotetrade guard)

      L     MW300                // candidate BCD word
      L     W#16#F000
      UW                          // upper nibble mask
      L     W#16#A000
      >I
      JC    INVALID
      L     MW300
      L     W#16#0F00
      UW
      L     W#16#0A00
      >I
      JC    INVALID
      L     MW300
      L     W#16#00F0
      UW
      L     W#16#00A0
      >I
      JC    INVALID
      L     MW300
      L     W#16#000F
      UW
      L     W#16#000A
      >I
      JC    INVALID
      L     MW300
      BTI                         // ACCU1-L: BCD -> INT
      T     MW310
      JU    DONE
INVALID: L     0
      T     MW310
DONE:  NOP 0

A more compact approach uses the S7 standard library FC for BCD validation when available, or a small custom FC called from every conversion site. Project hygiene: name the FC FC_BTI_SAFE or FC_BTD_SAFE and replace every bare BTI/BTD call with the wrapper. Place the FC in the master data library of the project so it is version-controlled with the application code.

6. S5TIME, S7TIME, and Counter Edge Cases

6.1 S5TIME preset violation

The legacy S5 timer instructions accept a time value in S5TIME format. The BCD range inside the time value is 0-999, and the time base in bits 12-13 must be 00, 01, 10, or 11. Errors are most often caused by:

  • Loading a time value calculated from an INT expression that exceeds 999 (e.g., L MW500; L 10; *I; SP T1 when MW500 is > 99 yields a BCD portion greater than 999).
  • Constructing the S5TIME word by OR-ing constants where the upper nibble is non-zero.
  • Loading a time value from a data block where the engineering value (seconds or minutes) was written by a HMI without scaling to the BCD range.

Fix: clamp the integer BCD portion to 999 before the SP instruction, or migrate the timer to an IEC TP/TON/TOF/TONR block which uses S7TIME and is not subject to BCD range limits.

6.2 S7TIME migration

Replace SP T1 with a call to IEC_Timer_0_Instant from the Standard Library > Timers > SFB3 (TP), SFB4 (TON), SFB5 (TOF). Set the time as S5TIME#2s200ms in the input. The value is stored internally as DINT milliseconds and never converted to BCD, so the 16#2521 trigger condition is removed for the time-value path. The trade-off is a small increase in cycle time (typically 5-15 microseconds per SFB call) and the loss of the legacy S5 timer word (T0-T255) visualisation in the standard STEP 7 online view.

6.3 Counter preset violation

Counters C0-C255 use BCD for the preset. The valid range is 0-999. Loading L C#1000 is rejected by the compiler; however, assembling a counter preset from a computed BCD word is the typical runtime cause. Apply the same BCD validation as in Section 5.1 before L C# or S C0 instructions. For applications requiring counts above 999, use the IEC counter SFB0 (CTU) or implement a software counter in a data block.

7. OB121 Behavior on S7-300 vs S7-400

OB121 is the programming error OB. With OB121 loaded, the CPU remains in RUN when a programming error fires. Without OB121, the CPU transitions to STOP. The SF LED is driven independently of the OB and is latched in the diagnostic buffer.

CPU OB121 Startup Info Open Block navigation Diagnostic buffer detail
S7-300 (firmware < V2.8) Limited; ACCU1 snapshot only Often greyed out 16#2521 with no block/address
S7-300 (firmware ≥ V2.8, e.g. 6ES7315-2EH13 V3.3) Block type + relative address Functional Full 16#2521 with network hint
S7-400 (all firmware) Block type + block number + address Functional Detailed 16#2521 with stack

A known S7-300 firmware quirk: on older CPU 31x-2DP part numbers (e.g., 6ES7315-2AG10 with firmware V2.0.0 to V2.6.x), the SF LED may latch even for transient BCD conversion events that occur only during a specific value transition. A documented case is the transition from value 7 to value 8 on a BCD-converted counter. The firmware misclassifies the transition as a conversion error and writes 16#2521 even though the actual values are valid. Loading OB121 suppresses the resulting STOP, but does not clear the SF LED. The correction path is a firmware update to V2.8 or later, available from the Siemens SIMATIC S7-300 support portal; confirm the current firmware version with PLC > Module Information > Identification before applying the update.

For S7-300 firmware ≥ V2.8, the OB121 local variables OB121_SW_FLT, OB121_RESERVED_1, OB121_FLT_REG, OB121_OB_NUMBER, OB121_BLK_TYPE, OB121_FLT_PRIORITY, OB121_FLT_BLK, OB121_MEM_AREA, OB121_MEM_ADDR, and OB121_BLK_NUM provide the triggering block type, block number, and byte offset. Program a temporary BE inside OB121 to read these tags in the online watch table when the next 16#2521 fires.

8. Clearing the Latched SF LED

After the offending instruction is fixed and downloaded to the CPU, the SF LED may still be latched. The clearing procedure is:

  1. Confirm the program change is in the online CPU. Right-click the block in the project tree and choose PLC > Download.
  2. Open Module Information > Diagnostic Buffer.
  3. Click Clear Buffer (or close the dialog and use PLC > Clear/Reset > Clear Buffer). The buffer is wiped.
  4. Cycle the offending instruction once (e.g., force the data word through a value that previously triggered 16#2521). The diagnostic buffer should remain empty for that instruction.
  5. Power-cycle the CPU only if the SF LED is still on after a buffer clear. The SF LED latch is reset by a STOP-RUN transition; an MRES (memory reset) clears all retentive data and is generally not required.
Warning: MRES resets the work memory including the program. Only use MRES when the project on the programming device is up to date and reload is planned. The safer path for clearing the SF LED is STOP-RUN transition (toggle the mode switch to STOP then back to RUN) followed by a buffer clear. Holding the mode switch in MRES position for 3 seconds then releasing to STOP performs a memory reset; this is the S7-300 procedure from the CPU 31xC and CPU 31x installation manual.

9. Verification

After the fix is applied and the LED is cleared, verify the system by:

  1. Re-creating the exact input sequence that produced 16#2521 (force a value of 1000 into the data word used by the suspect ITB, or a value with hex A-F into the suspect BTI source).
  2. Confirming the diagnostic buffer contains no new 16#2521 entry.
  3. Confirming the SF LED is off and remains off for at least one complete scan cycle plus a forced 0-9999 sweep of the suspect data word.
  4. Documenting the affected block, network, and data tag in the project change log.
  5. Running the program under S7-PLCSIM with the same forced values to confirm the fix is portable and the simulation does not produce 16#2521.
  6. Archiving the corrected S7 program and updating the project revision history.

10. Prevention and Best Practices

  • Replace legacy S5 timers (SP, SE, SD, SS, SF) with IEC timers (SFB3 TP, SFB4 TON, SFB5 TOF) to eliminate the S5TIME BCD constraint from the timer path. The migration cost is one block rewire per timer and the resulting SFB instances (DB per timer) must be sized accordingly.
  • Wrap every ITB, DTB, BTI, BTD in a project-standard FC that performs range validation and clamping. Reference the wrapper by name in all future code. Add a project-wide cross-reference check that flags direct calls to ITB/DTB/BTI/BTD outside the wrapper.
  • When declaring BCD tags, use the data type WORD and document the intended BCD format in a comment. Reserve true BCD-only tags (e.g., for panel display) for the BCD wrapper FC path.
  • Avoid loading binary INT values into a tag intended for BCD conversion. Type-checking at the FC boundary catches the pseudotetrade before it reaches the instruction.
  • For S7-300 CPUs running firmware older than V2.8, plan a firmware update via the SIMATIC Manager > PLC > Update Firmware workflow. The update package is supplied through the Siemens S7-300 manual and firmware download portal.
  • Set the diagnostic buffer size on the CPU to at least 100 entries (CPU Properties > Diagnostics/Clock). This preserves more 16#2521 history when investigating intermittent faults.
  • Configure HMI tag scaling to match the BCD range at the panel, not at the PLC. A panel that writes 0-10000 engineering units to a tag used in an ITB block is the most common upstream cause of the 16#2521 trigger.
  • When migrating from STEP 7 V5.x to TIA Portal, re-test all BCD conversion paths because the TIA Portal compiler accepts the same syntax but the S7-300/S7-400 firmware validation behaviour remains the same.

11. Troubleshooting Matrix

Symptom Likely cause Verify Fix
SF LED, 16#2521, ITB in user program Source INT > 999 Monitor source MW in online view, force 0-9999 sweep Clamp source to ≤ 999 before ITB
SF LED, 16#2521, BTI in user program Source contains hex A-F View source in hex, look for A-F nibbles Validate each nibble, replace invalid with 0
SF LED, 16#2521, SP T1 in user program S5TIME BCD value > 999 or invalid time base Online view the S5TIME word in hex Clamp value, fix time base bits
SF LED only on value transition 7→8 S7-300 firmware bug, older CPU 31x-2DP Check CPU firmware version in Module Information Update firmware to ≥ V2.8, ensure OB121 loaded
SF LED, no 16#2521, but OB80 time error Different fault class, not BCD Inspect full diagnostic buffer Resolve OB80 (time-of-day interrupt overflow)
SF LED, 16#2521, counter preset C# value > 999 or BCD tag with hex A-F Online view of preset word Clamp to ≤ 999 and validate BCD
SF LED appears only after HMI writes a value HMI scaling mismatches BCD range Inspect HMI tag properties and limit values Adjust HMI scaling to match BCD boundary
SF LED, 16#2521, DTB on real-value conversion Real value > 9,999,999 Online view of MD source in REAL then ROUND Clamp REAL conversion or split into word pair

12. Frequently Asked Questions

What is event ID 16#2521 on a Siemens S7-300 CPU?

Event ID 16#2521 is the diagnostic buffer entry for a BCD conversion error. It is raised by the CPU's instruction interpreter when an ITB, DTB, BTI, BTD, S5 timer load, or S7 counter preset instruction receives a value that cannot be encoded in BCD. The event is written to the diagnostic buffer, the SF LED is latched, and the programming error OB121 is called.

What is the maximum value for ITB (Integer to BCD) on S7-300?

The maximum encodable value for ITB is +999 and the minimum is -999. Values outside the range +/-999 trigger event 16#2521. The sign occupies bits 12-15 of the BCD word, leaving three decimal digits (bits 0-11). For the 32-bit DTB instruction the maximum is +/-9,999,999.

How can I clear the SF LED after fixing the BCD conversion error?

Download the corrected block, open PLC > Module Information, clear the diagnostic buffer, and toggle the CPU mode switch from RUN to STOP and back to RUN. The SF LED latch is reset by the STOP-RUN transition. MRES (memory reset) is not required and should be avoided because it clears the work memory.

Why does my S7-300 CPU 315-2DP raise 16#2521 only on a specific value transition such as 7 to 8?

Older S7-300 CPU 31x-2DP part numbers (for example 6ES7315-2AG10 with firmware V2.0.0 through V2.6.x) contain a firmware bug that misclassifies certain value transitions near BCD boundaries as conversion errors. Loading OB121 prevents the resulting STOP, but the SF LED still latches. The fix is a firmware update to V2.8 or later, available from the Siemens S7-300 support portal.

Does loading OB121 clear the SF LED for a 16#2521 BCD error?

No. OB121 only prevents the CPU from going to STOP. The SF LED remains latched and a 16#2521 entry is appended to the diagnostic buffer every time the offending instruction executes. The LED is cleared only after the instruction is fixed and the buffer is cleared (or the CPU is cycled through STOP-RUN).

Can I use the same wrapper FC for BCD validation on both S7-300 and S7-400?

Yes. The ITB, DTB, BTI, BTD opcodes have identical operand encodings and trigger conditions on both families. A single wrapper FC compiled with STEP 7 V5.5 runs on either. The only difference is the OB121 startup information depth (S7-400 provides more), so the same wrapper can be reused in either project.

Back to blog