Overview of SFC 1 READ_CLK on Siemens S7-300/400
SFC 1 (READ_CLK) is a Siemens standard system function available in the S7-300 and S7-400 CPU operating system that reads the current CPU clock and returns it as a BCD-encoded DATE_AND_TIME (DT) value. The block is part of the System Functions library that ships with every STEP 7 Classic installation and is mirrored in the STEP 7 V5.5 System and Standard Functions Reference Manual.
The function is invoked synchronously from user code (OB1, FB, FC, OB10–OB17, OB35, OB82, OB100–OB102, OB121) and writes 8 bytes of date/time information to the address pointed at by the OUT parameter. The block also returns an RET_VAL of type INT that contains the operation status.
SFC 1 is also re-implemented in third-party softPLC controllers that emulate the Siemens S7 instruction set, including the Advantech ADAM-821x CPU manual which lists SFC 1 through SFC 4 (READ_CLK, SET_RTM, CTRL_RTM, READ_RTM) as compatible system functions.
Prerequisites
- STEP 7 V5.x (or compatible) with the Standard Library → System Function Blocks container visible in the project.
- CPU firmware ≥ the version that ships with the targeted STEP 7 service pack. SFC 1 has been present in every S7-300/400 CPU since the original release; no firmware upgrade is required.
- A configured S7 station with a CPU whose internal clock is set (use SFC 0 SET_CLK or the PLC → Set Time of Day menu in SIMATIC Manager if not).
- A source block (OB1, FB, FC) from which SFC 1 will be called.
- Optional: a data block (DB) declared with a symbolic name so the date/time can be referenced symbolically.
DATE_AND_TIME Data Format (8-Byte BCD)
The output of SFC 1 is always an 8-byte DT value. The structure is byte-aligned and stored in BCD. Each byte holds two decimal digits except byte 7 which holds milliseconds (3 digits) plus weekday.
| Byte | Field | Range | Encoding |
|---|---|---|---|
| 0 | Year | 1990–2089 | BCD: 90 = 1990, 89 = 2089 |
| 1 | Month | 01–12 | BCD |
| 2 | Day | 01–31 | BCD |
| 3 | Hour | 00–23 | BCD |
| 4 | Minute | 00–59 | BCD |
| 5 | Second | 00–59 | BCD |
| 6 | Milliseconds (low + high nibbles) | 000–999 | BCD, high nibble reserved = 0 |
| 7 | Milliseconds (low nibble) + Weekday | ms 0–9 / 1=Sun … 7=Sat | BCD weekday in high nibble |
STEP 7 provides the IEC standard functions FC 3 D_TOD_DT and the conversion blocks in the Standard Library → IEC Function Blocks for converting the DT buffer into separate DATE, TIME_OF_DAY, and integer fields. The conversion is mandatory because the raw buffer cannot be used directly for arithmetic; arithmetic operators only work on the converted types.
RET_VAL Status Word
| W#16# | Meaning | Remedy |
|---|---|---|
| 0000 | No error | Continue |
| 8081 | Time not set / invalid (S7-400 only) | Call SFC 0 SET_CLK with valid DT, or correct via HMI |
| 80A1 | Wrong parameter type at OUT (e.g. type smaller than 8 bytes) | Change variable to DATE_AND_TIME / 8-byte buffer |
| 8xyy | General system error (refer to STEP 7 manual for the specific y/z nibble) | Consult online help for that error code |
Storage Locations for the Output
SFC 1 accepts the following OUT destinations, all of which must reserve 8 contiguous bytes:
-
Local TEMP variable in OB1, OB10, OB35, OB100, FB, FC. Declared as
DATE_AND_TIMEin the interface temp section. This is the default when you drag the block from the library into a network. -
Static variable in an FB instance DB. Declared as
DATE_AND_TIMEunder the stat section. Survives the call lifetime. -
Global DB variable of type
DATE_AND_TIME. The most common pattern for persistent storage that is also accessible from HMI tags. - Merker (M) area via area-crossing pointer. STEP 7 will only allow this if you pass an ANY pointer in STL; LAD/FBD will not display a Merker address directly because the variable must be DT.
DATE_AND_TIME (for example DWORD, REAL, STRING) will either be rejected at compile time or cause undefined behavior. The only valid ladder-compatible types are DATE_AND_TIME or a user-defined UDT of length 8 bytes.Step 1: Create the Destination DB with a Symbolic Name
The most common stumbling point in the field is assigning the DT value to a variable inside a data block. STEP 7 distinguishes between the absolute DB number and the symbolic DB name; without a name, symbolic component access is impossible.
- In the S7 Program / S7-300 station container, right-click Blocks → Insert New Object → Data Block.
- Choose type DB (not instance DB). Address is auto-numbered, e.g.
DB 2. - Open the Properties of the new block (right-click → Object Properties → General – Part 2) and enter the symbolic name, e.g.
ClockDataordata. Save. - Open the DB editor and declare a single variable:
NAME : read_time TYPE : DATE_AND_TIME START : 0.0 (byte offset) LENGTH : 8 bytes COMMENT : PLC date/time returned by SFC 1 - Save and compile the DB.
Without step 3 the symbolic name "read_time" will not resolve to a symbol table entry and STEP 7 will display "Symbol read_time is not a component of DB" when you try to use DB2.read_time on the OUT pin of SFC 1.
Step 2: Call SFC 1 in OB1
Open OB1 in LAD or FBD. Drag System Function Blocks → SFC 1 (READ_CLK) from the library into Network 1. STEP 7 will auto-create a TEMP variable of type DATE_AND_TIME for the OUT pin.
Replace the auto-generated TEMP variable with the symbolic DB address:
- Click the OUT pin and overwrite
#temp_varwith"ClockData".read_time(or whatever symbolic name you assigned). STEP 7 will then display the full pathDB2.read_timein the network view. - Alternatively, leave the address in absolute form:
DB2.DBX0.0with implicit length 8 bytes. STEP 7 inserts the proper ANY pointer internally.
Network 1 in STL will look like:
Network 1: Read CPU clock into DB2.read_time
CALL SFC 1
RET_VAL : MW100 // error word (INT)
OUT : "ClockData".read_time // DT, 8 bytes
Compile and download OB1 plus DB2. On the next OB1 cycle, SFC 1 will populate ClockData.read_time with the current clock value.
Step 3: Symbolic Access Without Renaming the DB
If you cannot or do not want to change the symbolic name of the DB, you can still access the variable symbolically by addressing the DB number directly and the symbol defined inside it:
DB2.read_time // absolute DB + symbolic component
The two halves of the dotted name are independent: DB2 refers to the absolute number; read_time is resolved from the symbol table of DB2. As long as the DB contains a row named read_time, this syntax works without changing the DB's own symbolic name. The dot-notation is also valid for instance DBs.
Step 4: Absolute Addressing Using the P# Pointer
For STL programmers, or when the DB symbol table is intentionally minimized, the ANY pointer syntax provides byte-precise control over the destination:
Network 1 (STL): Read clock into DB2 starting at byte 0
CALL SFC 1
RET_VAL : MW100
OUT : P#DB2.DBX0.0 BYTE 8
The P#DB2.DBX0.0 BYTE 8 ANY pointer explicitly specifies a length of 8 bytes. Without the BYTE 8 clause, the compiler may reject the call because the destination length is undefined.
Equivalent constructs using STL mnemonics:
L P##ClockData.read_time // load ANY pointer
LAR1 // into AR1
CALL SFC 1
RET_VAL : MW100
OUT : [AR1,P#0.0]
This pattern is useful in a generic FB that can read into any DB supplied by the caller.
Step 5: Cross-Area Storage (DB to Merker or Local)
Because the OUT parameter is typed as ANY pointer in the system interface, the destination may be located in any memory area supported by ANY pointers. The most common cases:
| Target area | Pointer form | Typical use |
|---|---|---|
| Local TEMP |
P#L 0.0 BYTE 8 (resolved automatically when you type a TEMP name) |
Short-lived buffer inside a single cycle |
| Instance DB static | P#DB100.DBX10.0 BYTE 8 |
FB with embedded timestamp |
| Global DB | P#DB2.DBX0.0 BYTE 8 |
HMI-visible persistent tag |
| Merker | P#M 100.0 BYTE 8 |
Passing timestamp to legacy FCs that expect raw bytes |
| Process I/O | not allowed | — |
Setting the Clock with SFC 0 SET_CLK
Once the read is verified, the inverse function SFC 0 (SET_CLK) lets you write the clock back, for example after a battery replacement or to synchronize to an HMI command. The interface is identical to SFC 1 except the parameter is named IN rather than OUT:
Network 5: Set CPU clock from DB2.read_time
CALL SFC 0
RET_VAL : MW102
IN : "ClockData".set_time // DT, 8 bytes
SFC 0 also returns W#16#0000 on success and may return W#16#8081 if the input DT is invalid (year < 1990 or > 2089, month > 12, etc.). Combined with SFC 1 in a coordinated block, you can implement pass-through synchronization between a master clock (PC, GPS receiver, or another PLC) and the S7 CPU.
Verification Procedure
- Download OB1 and DB2 to the CPU. Place the CPU in RUN.
- Open the DB2 online view in STEP 7. The
read_timefield should update every OB1 cycle with the current BCD-encoded date/time. - Cross-check against the operator panel: PLC → Set Time of Day → Read displays the same values in human-readable form.
- Force RET_VAL to
MW100in VAT and confirmW#16#0000. - Watch the values for at least one minute and confirm that the seconds counter increments at 1 Hz.
- Optional: trigger SFC 0 from a watch table to set the clock to a known value, then read back via SFC 1 and confirm round-trip accuracy to within 1 second.
Troubleshooting Matrix
| Symptom | Likely cause | Fix |
|---|---|---|
| "Symbol read_time is not a component of DB" | DB has no symbolic name; component not resolved | Open DB Properties and assign a symbolic name; recompile |
| Compile error: type conflict at OUT | Destination declared as DWORD, STRING, or smaller than 8 bytes | Change variable type to DATE_AND_TIME or expand to 8 bytes |
| OB1 goes to STOP, SF LED on | RET_VAL shows W#16#8081; CPU clock not set | Use SFC 0 SET_CLK or PG/HMI to set the time, then cold restart |
| Seconds do not change | SFC 1 not actually called (network disabled or wrong block instance) | Verify call in online view; check that the FB instance DB is being executed |
| Date shows 90-01-01 permanently | Battery removed; CPU lost clock | Replace battery, set clock via SFC 0 |
| Watch table shows only one byte update | Variable typed as BYTE not DATE_AND_TIME | Re-declare as DT, recompile and download |
| HMI tag stays at zero | HMI tag points to DB absolute byte; offset misaligned | Recreate tag at DB2.DBX0.0 with length 8 bytes |
Performance and Cycle-Time Impact
SFC 1 is implemented in firmware and executes in microseconds. Calling it once per OB1 cycle adds well under 50 µs to the cycle time on a typical S7-315 or S7-416 CPU. The function is re-entrant, so multiple call sites within the same OB1 do not interfere.
If you need to timestamp many events per cycle, prefer a single SFC 1 call at the top of OB1 into a global DT buffer, then copy bytes to per-event tags using BLKMOV or FC 5 (a copy of the DT body). This minimizes the number of firmware calls and keeps the cycle time deterministic.
Cross-Platform Compatibility Notes
SFC 1 is part of the documented Siemens S7 instruction set and is emulated by several softPLC platforms:
- Siemens WinAC RTX: identical semantics; same DT layout.
- Advantech ADAM-821x series: documented in the Advantech CPU 821x manual; the same BCD encoding and RET_VAL codes apply. The hardware does not have a battery-backed RTC, so the clock must be initialized at every power-up via SFC 0.
-
S7-1200 / S7-1500: SFC 1 does not exist in the S7-1200/1500 instruction set. Use the
RD_SYS_Tinstruction (S7-1500) orDTLdata type for analogous functionality. Migrating SFC 1 user code to S7-1500 requires manual conversion because the DT format is replaced by the more readable DTL structure.
Frequently Asked Questions
Why does STEP 7 reject "DB2.read_time" at the OUT pin of SFC 1?
The DB2 block must have a symbolic name in its Properties dialog. STEP 7 then resolves DB2.read_time through the symbol table; without the DB name, the component lookup fails with "Symbol read_time is not a component of DB".
Can I store the SFC 1 output in a Merker (M) area?
Yes, but only by using an ANY pointer in STL: OUT : P#M 100.0 BYTE 8. LAD/FBD will not display a Merker destination directly because the variable type cannot be DT; you must use the symbolic or absolute pointer form.
What is the difference between DATE_AND_TIME and DTL?
DATE_AND_TIME is an 8-byte BCD buffer used in S7-300/400. DTL is a 12-byte structured type used in S7-1200/1500 with separate year, month, day, hour, minute, second, nanoseconds fields. SFC 1 cannot be used with DTL; use RD_SYS_T on S7-1500 instead.
How do I read only the seconds from the SFC 1 buffer?
Use FC 6 (DT to components) or write a small STL block that masks byte 5 of the buffer and converts BCD to integer with FC 93 (BCD to INT). For HMI display, expose the full DT buffer and let WinCC or TIA Portal split it.
Does SFC 1 work after a battery fault on an S7-300?
If the CPU clock has been lost, SFC 1 still executes but the buffer will contain the default value DT#90-01-01-00:00:00.000 (year 1990, January 1). Initialize the clock with SFC 0 (SET_CLK) or via the PG online menu before relying on the timestamp.