S7-300/400 Counter Retentivity: Fix OB10 Daily Reset Value Loss

David Krause15 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

Problem Overview

On Siemens SIMATIC S7-300 and S7-400 controllers, an OB10 time-of-day interrupt is commonly used to reset production counters at a fixed clock time (for example, 00:00, 06:00, or shift handover). The interrupt fires reliably, but engineers frequently observe a secondary failure mode: after an uncontrolled stop–start cycle (power loss, STOP–RUN toggle, MRES reset, or firmware download), only some of the counters retain their last value. The rest fall back to zero. In a typical installation this presents as 70–90 % of the 98 counters in the user DB keeping their values, while a smaller subset loses them on every restart.

The root cause is almost never the OB10 logic itself; the daily reset continues to operate. The real defect is that the counter storage locations are sitting in memory areas the CPU is configured to treat as non-retentive. As soon as the CPU performs a cold restart, the non-retentive image is overwritten with the initial values declared in the data block, and the counters that were written into a retentive sub-range survive. This article documents how to identify the affected area, configure retentivity in STEP 7 V5.x HW Config and in TIA Portal device properties, and verify the result on a live CPU.

Root Cause: Why Some Counters Reset and Others Do Not

The S7-300/400 CPU distinguishes three power-up classes defined in the Siemens operating system manual:

Restart Class Retentive Bits Retentive Timers/Counters Retentive DB Contents User Program Execution
Cold restart (power on, MRES, download) Cleared Cleared Reset to initial values OB102
Warm restart Retained Retained Retained OB100
Hot restart Retained Retained Retained OB101 (S7-400 only)

If a counter is implemented as a DB variable of type INT or DINT that is incremented by an IEC counter function block (CTU, CTUD), the value lives in the load memory image of the DB. Whether that image is copied back into work memory on restart depends entirely on the retentivity setting in the CPU's hardware configuration. Counters implemented with the older S_CU / S_CD / S_CUD instructions sit in the system memory area of counters (C0…C255) and follow the global retentivity bit mask in HW Config, which is applied uniformly to the whole C area. The two implementation styles therefore respond differently to the same restart class.

When a mix of IEC-counter-DB tags and legacy S counters are present, the asymmetry is the source of the user's observation. The IEC counters whose DB word is inside the retentive range keep their count; the ones outside that range fall back to the initial value declared in the DB. The legacy S counters outside the retentive C range fall back to zero, regardless of DB placement.

Critical: A restart of the CPU does not clear the load memory of the S7-300. It clears the work memory copy. The values that "come back" are the values that the CPU re-reads from the MMC/load memory into work memory. If the load memory image itself was never updated (because the code only modifies a copy in work memory and never calls a retentive-aware instruction), the initial values from the offline project will be loaded instead.

S7-300/400 Memory Model and Retentivity Ranges

Every S7-300/400 CPU allows the engineer to declare retentive memory ranges in three categories:

  1. Memory bits (M area) — bit, byte, word, and double-word markers.
  2. Timers (T area) — applies to legacy S5 timers (S_PULSE, S_PEXT, etc.).
  3. Counters (C area) — applies to legacy S5 counters (S_CU, S_CD, S_CUD).

For S7-300 CPUs the retentive range is configured per byte count (for example, MB0…MB15 retentive = 16 bytes of M area). For S7-400 CPUs, individual bit, byte, word, and double-word markers can be marked retentive with finer granularity. The exact maximum retentive count for a given CPU is published in the device manual; for example:

CPU Order No. (MLFB) Max Retentive Merkers Max Retentive S5 Counters Max Retentive S5 Timers
CPU 312 6ES7312-1AE14-0AB0 16 bytes 8 0
CPU 315-2 DP 6ES7315-2AH14-0AB0 16 bytes 64 0
CPU 317-2 PN/DP 6ES7317-2EK14-0AB0 256 bytes 256 0
CPU 319-3 PN/DP 6ES7319-3EP10-0AB0 2048 bytes 2048 2048
CPU 412-2 6ES7412-2EK06-0AB0 4096 bytes (granular) 2048 2048
CPU 416-3 6ES7416-3ES06-0AB0 16384 bytes (granular) 2048 2048

The official S7-300 CPU 31x manual documents these limits in section 4.5 (" retentive and non-retentive memory areas"). For IEC counter blocks whose internal CV (current value) lives in a DB, the retentive flag on the DB word in the offline DB declaration does not, by itself, control cold-restart behaviour. The DB itself must be marked non-retain in its attributes, and only the byte range selected under "Retentive memory areas" of the CPU is restored. To retain a value inside a DB, the simplest model is to copy the value to a retentive M area and back on warm restart, or to make the entire DB retentive by including the DB in the CPU's "Retentive DB" list (S7-400 only, in the object properties of the CPU under "Retentive Memory").

Configuring Retentivity in STEP 7 V5.x HW Config

For an S7-300 with 98 counter tags in a single shared DB, follow this procedure:

  1. Open SIMATIC Manager > HW Config and double-click the CPU icon (e.g. CPU 315-2 DP / 6ES7315-2AH14-0AB0).
  2. Switch to the "Retentive Memory" tab in the CPU properties dialog.
  3. Set Number of memory bytes starting at MB0 to the byte count required. Each IEC counter increment can be packed as 4 bytes (DINT) or 2 bytes (INT); 98 counters at 4 bytes = 392 bytes. If the total exceeds the CPU maximum, the field displays a red value. In that case split the counter DB into a retentive part (most recent shift) and a non-retentive part (live count) and accept that the live-count part restarts from its initial value — which is correct for daily-reset counters anyway.
  4. Set Number of S5 counters starting at C0 to cover the legacy S counter range in use (e.g. 0–63 if you have 64 S_CU instances).
  5. Set Number of S5 timers starting at T0 only if any S5 timers are used; for OB10-driven resets it is usually 0.
  6. Click OK, then Save and Compile (Station > Save and Compile).
  7. Download the hardware configuration to the CPU. The CPU will report STOP briefly while the new system data is written to the MMC.

For an S7-400, the same dialog offers an additional "Retentive data blocks" list. Add the counter DB by DB number here if you want its entire image to be reloaded on warm restart.

Note: Increasing the retentive byte count reduces the available work memory for the user program. On a CPU 312 the limit is 16 bytes; attempting to set 392 bytes will be rejected with "Value out of range". In that case replace the S5-style counters with IEC counters and store the values in a retentive M area or a separate retentive DB on an S7-400.

Configuring Retentivity in TIA Portal

For S7-300/S7-400 projects migrated to TIA Portal V15 or later:

  1. In the project tree, expand PLC_1 > Device configuration and double-click the CPU.
  2. In the device view, click the Properties tab in the inspector window.
  3. Navigate to General > Retentive memory (S7-300) or Retentive memory areas (S7-400).
  4. For S7-300: enter the number of retentive Memory bytes, Counters, and Timers as decimal values.
  5. For S7-400: tick the checkboxes for the individual MB / MW / MD / C / T ranges to mark them retentive. The dialog updates the totals at the bottom.
  6. For IEC counter blocks (CTU, CTUD) whose CV lives in a DB: open the DB properties and ensure the "Retain" attribute is enabled, and the DB is listed under "Retentive data blocks" in the CPU properties (S7-400 only). On S7-300 the entire DB image is treated as non-retentive; only the M area carries across a cold restart.
  7. Compile the hardware (Project tree > PLC_1 > Compile > Hardware and Software (rebuild all)) and download to the CPU.

OB10 Time-of-Day Interrupt Setup

OB10 is the time-of-day interrupt organization block. It runs once when the configured time is reached, then waits for the next configured interval. The configuration parameters live in the CPU's Time-of-Day Interrupts tab.

Parameter Typical Value Meaning
OB number 10 Bound to OB10 in the user program.
Priority 2 (default) Lower than OB1 (1). Raise only if the reset must preempt cyclic execution.
Active The interrupt is enabled.
Execution "Once" / "Every minute" / "Every hour" / "Every day" / "Every week" / "Every month" For a daily reset choose Every day.
Start date / time 01.01.2024 00:00:00 First execution. Adjust to local time zone and DST.
Phase offset 0 ms Spread multiple OBs apart; not used here.

A minimal OB10 implementation that resets 98 INT counters stored in DB100 at offsets 0.0 through 194.0 looks like this in STL:

ORGANIZATION_BLOCK OB10
TITLE = "Daily counter reset"
VERSION : 0.1
  VAR_TEMP
    i : INT;
  END_VAR
BEGIN
  FOR i := 0 TO 97 DO
    "DB_Counter".C[i] := 0;
  END_FOR;
  // optional: stamp the shift tag
  "DB_Counter".LastReset := DT_DATE_AND_TIME;
END_ORGANIZATION_BLOCK

And in structured text (SCL) for TIA Portal:

FOR #i := 0 TO 97 DO
  "DB_Counter".C[#i] := 0;
END_FOR;
"DB_Counter".LastReset := DT_AND_TOD();

The reset itself is not the problem. The reset writes zero to a memory location that should still be retentive, so the next cold restart correctly reads the last zeroed value back. The asymmetry arises only when the increment logic writes to a non-retentive tag and the daily-reset logic writes to a retentive tag, or vice versa. Consistency is the goal.

Counter Implementation Patterns

Three patterns are in common use, each with a different retention behaviour:

Pattern Storage Location Retentive Control Best For
Legacy S_CU in STL/LAD C0..C255 system area "Number of S5 counters starting at C0" in HW Config Small count of fast, bit-style count events.
IEC CTU instance in DB CV of instance DB, then propagated to global DB Retentive M area or S7-400 retentive DB list Structured projects with FB-reuse.
Plain DINT in global DB DB word at fixed offset Retentive M area or S7-400 retentive DB list Daily-reset production counters in a flat DB.

For the scenario in the original question — 98 counters in a single FC, all in one DB — the third pattern is the simplest. Define a UDT UDT_Counter with Current : DINT; LastReset : DATE_AND_TIME; DailyPeak : DINT, instantiate it 98 times in a global DB, and reserve a matching byte range in the M area as a shadow buffer. On STOP→RUN transition, copy the shadow back into the DB; on OB10, zero the DB and write the shadow to zero as well.

Alternative Daily-Reset Strategies

If the S7-300's 16-byte retentive limit is too tight for 98 × 4-byte counters, consider:

  1. Move the historical counters to a separate S7-300 CPU with larger retentive capacity, or upgrade to a CPU 319-3 PN/DP (2 KB retentive markers).
  2. Use a retentive recipe DB on an S7-400 — DBs on S7-400 can be marked fully retentive in the CPU's "Retentive DBs" list.
  3. Persist to MMC and reload on OB100/OB102. On OB100 (warm restart) read the last value from a recipe-style DB. This works on S7-300 with firmware V2.0 and higher. Use SFB 81 RD_DPARA or direct load-memory reads via SFC 83 READ_OB (S7-400) to bring a snapshot back into work memory on cold restart.
  4. Shift to a TIA Portal S7-1500. The S7-1500's data block retentive mechanism is per-tag: each RETENT attribute on a DB tag is honoured across cold restart, with up to 8 MB of total retentive data on a CPU 1515-2 PN, for example. This is the most direct modern fix.

The fourth option is the most common engineering decision today because it removes the entire class of "some retentive, some not" bugs and replaces them with a per-tag flag that is hard to mis-configure. Reference the SIMATIC S7-1500 CPU 1515-2 PN manual for the retentive limits and the S7-1500 system manual for migration steps.

Verification and Commissioning

After the retentive range is configured, verify with this checklist before handing the system back to operations:

  1. In STEP 7, open the CPU properties and confirm the retentive byte count and counter count. Read back from the online CPU: PLC > Module Information > Retentive Memory.
  2. Increment a test counter manually with a VAT table (DB_Counter.C[0] := 12345).
  3. Stop the CPU with the mode selector. Wait 5 seconds. Switch back to RUN.
  4. Read the counter again. The expected value is 12345. If the value is 0, the DB is not in a retentive area or the OB10 has fired during the test (rare but possible if a time-of-day boundary falls inside the test window). Disable OB10 (uncheck "Active") before the test if you want a clean signal.
  5. Repeat for a counter at the end of the DB (DB_Counter.C[97]) to confirm the entire byte range is covered.
  6. Force a power-cycle of the cabinet (open the MCB feeding the 24 V DC PSU is not enough — you need a CPU power loss). Confirm that the values still come back.
  7. For a full audit, trigger a MRES reset. This clears the load memory too and is the worst case. The values will NOT survive MRES. Document this in the operator manual so it is not interpreted as a bug.
Field-proven caveat: A counter that lives inside a multi-instance DB (e.g. an FB-encapsulated CTU) and is later declared as non-retain on the FB will lose its value on STOP→RUN even if the parent DB is marked retain. The instance DB takes the setting of the FB that owns it. Walk the FB hierarchy from the counter all the way to the root DB before assuming "retain" is set.

Troubleshooting Matrix

Symptom Likely Cause Diagnostic Fix
All 98 counters reset to 0 on STOP→RUN Retentive byte count is 0; entire DB is non-retentive HW Config > Retentive Memory > "Number of memory bytes" Set retentive bytes to 392 (or use S7-400 retentive DB list)
Counters 1–70 retain, 71–98 lose value DB array exceeds retentive byte window; only the low-offset part is retained Compare DB layout vs. retentive byte count Move low-priority counters to a separate non-retentive DB
Legacy S_CU counters reset; IEC DB counters retain "S5 counters starting at C0" is 0; S counters default to 0 on cold restart HW Config > Retentive Memory > Counter count Set S5 counter count to cover the highest C index used
Values lost after MRES only MRES clears load memory; retentive flags are checked but the source is gone Confirm mode selector position and operator action Use STOP→RUN or warm restart; document MRES as destructive
OB10 fires at the wrong time CPU clock not synchronised; DST shift; wrong time format (local vs. UTC) Online > Set Time of Day; check PLC > Time Use NTP or SFC 0 SET_CLK with a master clock; document the time base
OB10 does not fire at all "Active" checkbox is off; priority conflict; OB10 not downloaded CPU diagnostic buffer; HW Config > Time-of-Day tab Enable OB10, recompile, redownload hardware
Counter overflow at INT limit Counter type is INT (–32768…+32767) Cross-check; observe negative count Change to DINT in the UDT

Best Practices for Daily-Reset Counter Banks

  • Use a UDT for the counter record and instantiate it in a single DB. Versioning the UDT is easier than versioning 98 separately declared tags.
  • Stamp the last reset time as DATE_AND_TIME (8 bytes) inside the same UDT. This makes "is today's count correct?" self-diagnosing on the HMI.
  • Centralise the reset in OB10 and have no other code write to the same DB byte range. Mixing cyclic and time-of-day writes is the most common cause of "values change at unexpected times".
  • Document the retentive boundaries in the project comments near the DB declaration. A future maintainer should not have to open HW Config to find out why counter 73 is special.
  • Use the S7 diagnostic buffer (event ID 4300 series for retentive memory changes, 0x4301 for STOP→RUN transitions) to confirm retentivity changes during commissioning.
  • Do not place production counters in the same DB as setpoints and recipes. A recipe download will rewrite the entire DB with the offline project values, wiping the live counters. Split counters into their own DB.

FAQ

Why do only some of my 98 counters reset on a PLC restart when OB10 is configured?

OB10 is unrelated to the restart behaviour. The asymmetry is caused by the counter tags sitting partly inside and partly outside the retentive byte range declared in HW Config. On cold restart the CPU reloads the retentive sub-range from the MMC; everything else is overwritten with the DB's initial values. Increase the retentive byte count under CPU properties > Retentive Memory, or split the DB so the part that must survive a power-cycle is in a retentive DB (S7-400) or a retentive M area.

How do I configure OB10 to fire once per day at midnight in STEP 7?

Open HW Config, double-click the CPU, go to the Time-of-Day Interrupts tab, set OB number 10, priority 2, tick Active, set Execution to "Every day", and set the start date/time to today's date at 00:00:00. In OB10 write the reset code (FOR loop over the counter array). Download both the hardware configuration and the OB10 to the CPU.

What is the maximum retentive memory on an S7-300 CPU 315-2 DP?

The CPU 315-2 DP (6ES7315-2AH14-0AB0) supports up to 16 bytes of retentive memory bits, 64 retentive S5 counters, and 0 retentive S5 timers. For 98 DINT counters (392 bytes) you must either move to a CPU 319-3 PN/DP (2 KB retentive markers), an S7-400, or an S7-1500 with per-tag retentive flags.

Can I make a data block fully retentive on an S7-300?

No. The S7-300 only retains the M, T, and C areas; DB contents are not in the retentive list. The supported workaround is to copy the DB values into a retentive M area on every change (or every cycle) and copy them back on OB100/OB102. On an S7-400 you can add the DB to the CPU's Retentive DBs list. On an S7-1500 each DB tag can carry a RETENT attribute.

My counters survive STOP→RUN but disappear after MRES. Is that normal?

Yes. MRES performs a memory reset that clears both work and load memory; retentive flags do not help because the source image is gone. Treat MRES as a destructive operation and avoid it in production. If the values must survive even MRES, persist them to a recipe DB on the MMC and reload in OB100; the load memory is only cleared by an MMC card format or a download in STOP mode with "Delete all" enabled.

Back to blog