Problem Statement and Field Symptoms
On a SIMATIC S7-300 station built around a CPU 313C-2DP, an automation cell receives one 16-bit Process Data word (PZD) from a variable-frequency drive (VFD) over Profibus DP. The high byte holds a PID controller output scaled 0–100 % and the low byte holds drive status bits (run, fault, cam, etc.). The PZD lands in DB100.DBW60 and the engineer splits it into two markers with two MOVE boxes:
MOVE DB100.DBB60 → MW140MOVE DB100.DBB61 → MW142
While the machine is running the SF (System Fault) LED on the CPU is steady red. When the machine stops, the SF LED clears. STEP 7's diagnostic buffer records a BCD conversion error at intervals that match the drive's cycle time. The MOVE boxes are not BCD instructions, so the source of the error is not where the symptom looks.
This article walks through diagnosis of the BCD conversion error on the S7-300 CPU 313C-2DP family, the memory-layout pitfall that makes the symptom look like a MOVE problem, and a field-proven implementation for splitting the Profibus PZD word safely into two bytes without triggering the CPU's BCD check.
Applicable Hardware and Software
Confirm the part numbers and firmware before applying this procedure. The 313C-2DP catalog number stamped on the front of the module determines which manual and which firmware behavior applies:
| Catalog Number | Description | Firmware |
|---|---|---|
| 6ES7 313-6CF03-0AB0 | CPU 313C-2DP, 64 KB work memory, integrated Profibus DP master | V2.6.x |
| 6ES7 313-6CG04-0AB0 | CPU 313C-2DP successor, 64 KB work memory | V3.3.x |
| 6ES7 313-5BG04-0AB0 | CPU 313C (no Profibus) – reference only | V3.3.x |
| 6ES7 313-6BG04-0AB0 | CPU 313C-2 PtP – reference only | V3.3.x |
CPU 313C-2DP specifications that matter for this fix:
- Bit execution: 0.07 µs (CF03), 0.06 µs (CG04)
- Integrated Profibus DP master/slave: 9.6 kbps to 12 Mbps
- Digital I/O: 16 DI / 16 DO integrated
- Analog I/O: 4 AI / 2 AO integrated (11-bit + sign)
- Integrated counters: 3 channels up to 30 kHz
- Programming: STEP 7 V5.5 + SPx or STEP 7 (TIA Portal) V13+
Reference: SIMATIC S7-300 CPU 31xC and 31x: Manual.
Understanding the BCD Conversion Error
Binary Coded Decimal encodes each decimal digit in its own 4-bit nibble. The S7-300 CPU's BCD conversion family (BTI, ITB, BTD, DTB, BCD_I, I_BCD) expects every nibble in the source operand to be in the legal range 0000b to 1001b (decimal 0 to 9). When the CPU encounters any nibble in the illegal range 1010b to 1111b (decimal 10 to 15), the conversion result is undefined, the BR (binary result) bit is cleared, and the CPU raises a programming error.
Per the SIMATIC S7-300/400 standard function reference, the programming error is reported as:
- Fault code in the diagnostic buffer: 2521 (decimal) / 0x09D9 (hex) for BCD conversion error
- Fault class: programming error
- OB invoked: OB121 (Programming Error OB) if present
- SF LED: lit while the error is unacknowledged
Reference: STEP 7 V5.5 System and Standard Functions Reference Manual, section on programming-error OBs.
Why the Symptom Tracks Machine Run
The status byte from the drive contains bits that represent "running," "fault present," "at setpoint," etc. During machine stop the drive is in standby; most of those bits are 0 and the byte carries a low value such as 0x00, 0x01, or 0x02. None of those low values contain nibbles 0xA–0xF, so a BCD conversion on a stopped byte quietly succeeds.
During machine run, the drive sets bits that produce values such as 0x4F (running, at setpoint, fault latch cleared), 0xB7 (run enable plus warning), or 0xF0 (full status word). The 0xF in any of these nibbles triggers the BCD conversion error the moment another part of the program passes the byte through a BCD instruction. The SF LED lights in step with the run state because the data only becomes illegal when the drive is active.
Where the BCD Conversion Actually Lives
The MOVE boxes from the field report (DB100.DBB60 → MW140, DB100.DBB61 → MW142) are innocent. MOVE on S7-300 is a pure byte copy and never raises a BCD error. The fault is in one of the following locations:
-
Hidden BCD conversion block: A legacy FB written against an older drive mapping may still issue a
BTIon the data block word before splitting. Search the project forBTI,BCD_I,ITB,I_BCD,BTD,DTB. -
S5 timer / counter preset: S5 timers (
SP,SE,SD,SS,SF) and S5 counters (S_CD,S_CU,S_CUD) load their preset values in BCD. Loading a non-BCD marker word into a preset triggers the same fault. - HMI scaling block: A scaling FB that publishes status to the panel via a BCD conversion that was incorrectly retained from a S5-era codebase.
- Cross-block pointer arithmetic: A POKE or area-crossing move on the marker word that effectively transfers the byte into a region later used by a BCD instruction.
Reading the Diagnostic Buffer on the 313C-2DP
Use STEP 7 to query the CPU's diagnostic buffer, which is the authoritative record of faults:
- Open STEP 7 and go online (PLC > Connect to Target System > Accessible Nodes).
- Select the 313C-2DP from the discovered nodes.
- Open PLC > Module Information > Diagnostic Buffer.
- Read the most recent event of class Programming error. Look for "BCD conversion error" in the event text. Note the block number, block type (FB/FC/OB), and relative instruction offset shown in the event detail.
- Click the Open Block button to navigate STEP 7 directly to the failing network.
- Save the diagnostic buffer for the maintenance log: PLC > Module Information > Save As.
Reference: SIMATIC S7-300 Programming with STEP 7 V5.5, chapter on diagnostics and fault handling.
OB121 Local Variables for BCD Conversion Errors
OB121 receives fault information in its local temporary variables. Reading these variables in a logging block turns OB121 into a passive fault-trap. The variables of interest for a BCD conversion fault are:
| Local Variable | Type | Meaning on BCD Error |
|---|---|---|
| OB121_EV_CLASS | BYTE | Event class: B#16#35 (incoming fault) |
| OB121_SW_FLT | BYTE | Fault identifier (BCD conversion = 0x21 / 33) |
| OB121_PRIORITY | OB_PRIORITY | Priority of OB121 in the running priority class |
| OB121_OB_NUMBR | OB_NUMBER | 121 |
| OB121_BLK_TYPE | BYTE | Block type where fault occurred: 0x08 = OB, 0x0A = DB, 0x0C = FC, 0x0E = FB |
| OB121_BLK_NUM | WORD | Number of the failing block |
| OB121_PG_OFFSET | WORD | Relative instruction offset of the failing instruction |
| OB121_FLT_REG | DWORD | Contains the illegal BCD value read by the failing instruction |
Sample OB121 stub for logging the failing block to a watch DB:
FUNCTION_BLOCK FB_OB121_Logger
// Diagnostic stub - drop into OB121 to capture BCD fault context.
VAR_TEMP
info AT : STRUCT
EV_CLASS : BYTE;
SW_FLT : BYTE;
PRIORITY : BYTE;
OB_NUM : BYTE;
RESERVED1 : BYTE;
RESERVED2 : BYTE;
BLK_TYPE : BYTE;
RESERVED3 : BYTE;
BLK_NUM : WORD;
PG_OFFSET : WORD;
FLT_REG : DWORD;
END_STRUCT;
END_VAR
BEGIN
// info pointer is bound by STEP 7 to OB121's local stack
DB_BCDFaultLog.SW_FLT := info.SW_FLT;
DB_BCDFaultLog.BLK_TYPE := info.BLK_TYPE;
DB_BCDFaultLog.BLK_NUM := info.BLK_NUM;
DB_BCDFaultLog.PG_OFFSET := info.PG_OFFSET;
DB_BCDFaultLog.FLT_REG := info.FLT_REG;
END_FUNCTION_BLOCK
Cross-Referencing Every BCD Instruction in the Project
With OB121 in place, run the following audit against the offline project:
- Open the S7 program and select the Blocks container.
- Right-click and choose Reference Data > Display. Generate the cross-reference list.
- Filter by symbol or operand pattern
DB100.DBW60,DB100.DBB60,DB100.DBB61,MW140,MW142, and any of the legacy marker region around them. - Open every block in the resulting list and search inside the source for
BTI,BCD_I,ITB,I_BCD,BTD,DTB. STEP 7's cross-reference does not flag BCD conversions as data-flow violations; only a manual scan will. - Search for S5 timer / counter operations on the same markers:
SP,SE,SD,SS,SF,S_CU,S_CD,S_CUD. - For each occurrence, decide: (a) remove the BCD conversion, (b) replace it with type-correct integer conversion, or (c) add range validation before the conversion.
Memory Layout: Words, Bytes, and the Marker Overlap Trap
S7-300 marker memory is bit-addressable. Markers MB0 through MB2047 form the working area, and words are simply pairs of adjacent bytes. MW140 occupies MB140 (low byte) and MB141 (high byte). MW142 occupies MB142 and MB143. The bytes do not overlap because the marker word indices are even/odd, but if you reuse MW141 anywhere in the program, you corrupt the high byte of MW140.
The MOVE from DB100.DBB60 to MW140 writes only MB140. The MOVE from DB100.DBB61 to MW142 writes only MB142. The high bytes MB141 and MB143 retain whatever value they had before. If the program reads MW140 as a 16-bit word expecting a clean low byte, it can be confused by stale data in MB141.
If you need the marker word to be clean (low byte only, high byte zero), explicitly clear the high byte before the move, or use the STL mask pattern shown in the next section.
Step-by-Step: Splitting DB100.DBW60 into Two Marker Words
The procedure below uses an existing FC (here named FC100) and produces clean marker words without BCD conversion:
- Open the S7 program in STEP 7 and locate the FC that handles the Profibus data from the drive. If no such FC exists, create FC100.
- Insert the STL network below at the top of FC100 so it runs each OB1 cycle.
- Compile the FC and download the modified block to the 313C-2DP.
- Place the CPU in RUN with the drive enabled.
- Open a VAT (Variable Table) and add MW200, MW140, MW142. Force MW200 from the VAT to known values and confirm the masks work as expected.
- Drive a known pattern through DB100.DBW60 from the drive (or the simulation) and verify MW140 / MW142 track.
STL Implementation
// FC100 - Split DB100.DBW60 into two marker words
// MW140 = low byte (drive status flags)
// MW142 = high byte (PID output 0..100 %)
L DB100.DBW60 // Load full PZD word
T MW200 // Save full word for masking
L MW200 // Reload full word
L W#16#00FF // Mask = 0x00FF keeps low byte only
UW // AND word
T MW140 // Status flags
L MW200 // Reload full word
L W#16#FF00 // Mask = 0xFF00 keeps high byte only
UW // AND word
SRW 8 // Shift right 8 bits to align
T MW142 // PID output 0..100
NOP 0
LAD / FBD Implementation
In LAD or FBD, two MOVE boxes suffice when the destination marker word is otherwise unused:
[ MOVE ] DB100.DBB60 --> MB140
[ MOVE ] DB100.DBB61 --> MB142
If the high bytes MB141 and MB143 are reused elsewhere, follow each MOVE with a word MOVE that copies the byte into a clean word:
[ MOVE ] DB100.DBB60 --> MB140
[ MOVE ] 0 --> MB141
[ MOVE ] DB100.DBB61 --> MB142
[ MOVE ] 0 --> MB143
SCL Implementation
// SCL fragment - type-safe split
VAR_TEMP
pzd_word : WORD;
status_b : BYTE;
pid_b : BYTE;
END_VAR
pzd_word := "DB_Profibus".DBW60;
status_b := pzd_word AND W#16#00FF; // low byte
pid_b := WORD_TO_BYTE(SHR(IN := pzd_word AND W#16#FF00, N := 8));
// Publish to markers (MOVE equivalent)
MB140 := status_b;
MB142 := pid_b;
Handling Drive Mapping You Cannot Change
In the field case the drive's parameter mapping is locked (drive engineer off-site). The byte split must accommodate the existing mapping without modifying the drive. The pure masking approach above does exactly that: it does not touch the Profibus PZD structure or the data block layout. Only the marker consumption pattern changes.
If the receiving program needs the PID output as a 0–100 % normalized integer rather than a byte, the second stage converts the high byte to a 0–27648 standard PROFIdrive scaling. Avoid BTI in this stage; use direct integer arithmetic:
// PID output in 0..100 percent from high byte, no BCD
L MW142 // PID output 0..100
L 100 // divisor (engineering unit max)
*I // INT * INT, no BCD
T MW300 // result in engineering units
Removing the BCD Conversion Without Losing Function
Once the failing block and instruction are identified, three replacement strategies work in practice:
Strategy 1: Delete the conversion
If the original programmer's intent was only to display the status byte, no BCD conversion was ever needed. Status words from drives are bit patterns and should be parsed bitwise:
// Bit-parse the status byte
A MB140.0 // drive running
= M 100.0
A MB140.1 // drive faulted
= M 100.1
A MB140.2 // at setpoint
= M 100.2
// ... continue per drive manual
Strategy 2: Replace with INT conversion when needed for HMI
For HMI display of the PID output byte, use a direct integer move. The PID output byte is a small integer (0–100) and never triggers any BCD check on its own:
L MB142 // PID output byte
T MW310 // integer-ready for HMI
Strategy 3: Range-validation wrapper
If a BCD conversion is genuinely required (for example, driving an S5-format timer preset), wrap the source value with a range check that limits it to 0–999 before the BCD instruction:
// Range-limit before BCD conversion
L MW142
L 999 // ceiling
>I // clip high: keep below 999
SPB HIGH
TAK
L 0
>I // clip low: never negative
SPM LOW
SPA CONVERT
HIGH: L 999
SPA CONVERT
LOW: L 0
CONVERT:
BTI // now safe; nibbles all <= 9
T MW320
Verifying the Fix on the Live CPU
- Download the modified blocks. Confirm STEP 7 reports no compile errors and the online / offline compare matches.
- Reset the CPU (MRES) if a programming error response is latched. Note: MRES clears work memory, not the diagnostic buffer; the diagnostic buffer remains until manually cleared via STEP 7.
- Place the CPU in RUN with the drive enabled.
- Open the diagnostic buffer. Confirm no new BCD conversion entries appear over a 30-minute observation window covering at least one machine cycle.
- Open a VAT and watch MW140, MW142, DB100.DBW60. Trigger known drive events (run, fault, reset) and confirm the marker values track.
- Cycle power the 313C-2DP and confirm the SF LED remains off after restart.
- If OB121 was added for diagnostics, leave it in place but switch the diagnostic logging DB to a write-only structure to minimize scan time.
Troubleshooting Matrix
| Symptom | Likely Cause | Fix |
|---|---|---|
| SF LED lit, drive running, BCD error in buffer | BTI/BTD/ITB on the status byte or word | Locate offending block via OB121 / diagnostic buffer, replace or guard the BCD conversion |
| SF LED only while machine runs | Status byte carries illegal BCD nibbles only in run state | Mask the byte before any conversion; never BCD-convert status words |
| SF LED clear after STOP | CPU clears pending programming error on STOP | Add OB121 to keep diagnostic continuity across STOP/RUN transitions |
| SF LED latched after fix | Firmware-revision-dependent latch behavior on CF03 / CG04 | Power-cycle the 313C-2DP |
| MOVE of DBB to MW leaves stale high byte | MOVE only touches the low byte of the destination word | Use the STL mask pattern or move zero into the high byte |
| S5 timer preset with BCD conversion error | Preset loaded with a non-BCD value | Convert integer preset to BCD before the timer call, or use IEC timers (SFB3 / SFB4 / SFB5) |
| Marker overlap causes cross-block corruption | MW / MB allocation not reviewed after split | Generate cross-reference for the marker region and audit each usage |
| SF LED on, diagnostic buffer empty | Diagnostic buffer was manually cleared or CPU is in restart | Wait for next cycle and re-read; check PLC > Clear/Reset > Diagnostic Buffer |
Standards Reference
BCD-conversion behavior is standardized in IEC 61131-3 for PLC programming languages. Siemens STEP 7 implements the BCD-related conversion functions consistent with this standard:
| STEP 7 Name | IEC 61131-3 Equivalent | Behavior on Illegal Nibble |
|---|---|---|
| BTI | BCD_TO_INT | Programming error, BR = 0 |
| ITB | INT_TO_BCD | Programming error if INT > 9999 |
| BTD | BCD_TO_DINT | Programming error, BR = 0 |
| DTB | DINT_TO_BCD | Programming error if DINT > 99999999 |
Reference: IEC 61131-3, Programmable controllers – Part 3: Programming languages, section on conversion functions.
Related Field Notes
- The same fault pattern applies to the CPU 314C-2DP (6ES7 314-6CG03-0AB0 / 6ES7 314-6CH04-0AB0) and CPU 315-2DP / 315-2PN/DP family. The OB121 mechanism and BCD conversion behavior are common to the S7-300 line.
- For SINAMICS G120 / S120 drives over Profibus DP using the PROFIdrive profile, the status word (ZSW1) is mapped at PZD1 and is a bit pattern, never BCD. The PROFIdrive standard forbids BCD interpretation of PZD words.
- For drives that publish both control and feedback in a single packed word (as in this field case), confirm the byte order with the drive engineer before changing the masking pattern. Big-endian vs little-endian interpretations swap which byte is high and which is low.
FAQ
Why does the SF LED only appear when the machine is running?
The drive only writes illegal BCD nibbles (0xA–0xF) into the status byte while running. When stopped, the byte carries 0x00, 0x01, or 0x02, all of which are legal BCD. A BCD conversion on the running data triggers the fault; on the stopped data, it does not. This is why the SF LED tracks the machine run state directly.
Is the MOVE instruction actually at fault?
No. MOVE on the S7-300 is a pure byte copy and never raises a BCD conversion error. The diagnostic buffer points to a BCD instruction (BTI, ITB, BTD, DTB, BCD_I, I_BCD) in a different block. Search the project for these instructions against the data block region DB100.DBW60 and the marker region MW140 / MW142.
How do I find the failing block without a programmer?
Read the CPU's diagnostic buffer in STEP 7 (PLC > Module Information > Diagnostic Buffer). Each programming error entry includes the block number, block type, and relative instruction offset of the failing statement. Click "Open Block" to jump STEP 7 to the offending network. If OB121 is present, its local variables OB121_BLK_NUM and OB121_PG_OFFSET give the same data.
Should I add OB121 to the program or remove it?
For diagnosis, add OB121 (or a stub FB inserted into OB121) so the CPU does not transition to STOP on the first fault. Once the failing BCD conversion is removed, leave OB121 in place for future fault visibility, but switch the body to a lightweight log so it does not extend the OB1 scan.
What is the right CPU part number for the 313C-2DP used here?
The Profibus master variant is 6ES7 313-6CF03-0AB0 (firmware V2.6) or 6ES7 313-6CG04-0AB0 (firmware V3.3). Both expose integrated Profibus DP and respond identically to BCD conversion errors. Confirm the part number on the front of the module before applying any firmware-specific guidance.
Can S5 timers cause the same BCD error from a marker preset?
Yes. S5 timers and counters load their preset values in BCD. Loading a non-BCD marker word into the preset of an SP, SE, SD, SS, SF, S_CU, S_CD, or S_CUD operation triggers the same OB121 / BCD conversion error as a direct BTI instruction. Convert the preset to BCD before the call, or use the IEC equivalents SFB3 (TP), SFB4 (TON), and SFB5 (TOF), which accept integer presets directly.