Generating Random Numbers in S7-300 CPU Using STEP 7 FCs

David Krause12 min read
S7-300SiemensTutorial / How-to
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

Overview

The S7-300 family of CPUs (CPU 312 through CPU 319, including the F-variant failsafe and C-variant compact variants) does not ship with a native pseudo-random number generator block. Unlike the S7-1500 and S7-1200 lines, where the extended instruction set includes the Random block from the cryptography library, an S7-300 programmer must construct the generator inside a function (FC) or function block (FB) using the standard arithmetic instructions available in every 31x CPU. This article documents a field-proven approach using a Linear Congruential Generator (LCG) implemented in STL and SCL, then scales the resulting 32-bit DWORD to the IEEE-754 REAL format so the value can drive floating-point math, scaling, and simulation routines during program testing.

The approach below targets test-bench scenarios where a developer needs a value that:

  • changes on every PLC scan (or every call to the FC),
  • is uncorrelated with the process I/O,
  • spans a configurable floating-point range, including small decimals and large-magnitude numbers (for example, current readings in the order of 1e-3 to 1e+6),
  • is deterministic when seeded, so test runs remain reproducible.
Note on statistical quality. A Linear Congruential Generator is sufficient for engineering test data, simulation injection, and HMI value demonstration. It is not cryptographically secure and must not be used for safety-relevant decisions, lottery, or authentication applications. For those, use the S7-1500 Random instruction or an external deterministic random bit generator (DRBG) source.

Prerequisites

Item Requirement
CPU Any S7-300 CPU 31x with firmware ≥ V2.x (CPU 312, 313, 314, 315, 315-2 DP, 316, 318-2, 319-3)
Programming tool STEP 7 V5.5 + SP2 (or STEP 7 V5.6) with optional S7-SCL add-on
Instruction set used Standard bit/word/double-word arithmetic (L, T, *D, +D, DTR, /R)
Memory required 8 bytes of Merker/DB for the seed (DWORD), 4 bytes for the REAL output
OB context OB1 cyclic, OB35 cyclic interrupt, or OB100 startup (for seed initialization only)

Reference the S7-300 CPU 31x system manual for the instruction set and the SIMATIC S7-300 CPU 31xC and 31x: Technical specifications document for cycle-time impact of double-word multiplication on the specific CPU in use.

Why S7-300 Lacks a Native Random Block

The Random instruction was introduced as part of the TIA Portal extended instruction library for S7-1200 and S7-1500. The official documentation defines it as a 32-bit pseudo-random number generator that returns a value in the range 0 to 2,147,483,647 (positive INT range) and is intended for non-deterministic data such as challenge values or unique identifiers. See Random (Generate random number) — S7-1500 documentation.

The S7-300 firmware (the most current 31x firmware is the V3.x generation released for CPU 319-3 PN/DP and the 315-2 PN/DP) does not contain this block. STEP 7 V5.5 cannot insert Random into an S7-300 program. Engineers must therefore emulate the behaviour with the CPU's built-in 32-bit integer arithmetic. The LCG algorithm selected below produces a sequence with period 232, which is the maximum that a 32-bit state machine can reach before repetition.

Algorithm Selection: Linear Congruential Generator (LCG)

The LCG recurrence is:

Xn+1 = (a · Xn + c) mod 232

where:

  • a (multiplier) = 1664525 (0x0001965D)
  • c (increment) = 1013904223 (0x3D7D41A5)
  • mod 232 is performed implicitly by the S7-300 CPU's 32-bit register wrap on *D and +D double-word operations.

These are the constants proposed by Numerical Recipes and used in the reference C library nrand48. They produce a full-period sequence (period = 232) and pass the standard spectral test for at least the first six dimensions.

LCG Flow Inside an S7-300 FC Read Seed (MD x) DWORD input Multiply by 1664525 *D Add 0x3D7D41A5 +D (mod 2^32 implicit) Store new Seed T MD y Scale to REAL DTR + /R Output REAL value MD 200

STL Implementation in STEP 7 V5.5

The most portable form for the S7-300 is Statement List (STL). The complete FC follows. The seed lives in MD 100 (a Merker DWORD); the new value lands in MD 104; the REAL-scaled value is stored in MD 200.

Network 1 — LCG step (DWORD)

FUNCTION FC 100 : VOID
TITLE = Pseudo-random DWORD via LCG
AUTHOR : AUTOHOT
FAMILY : TESTTOOLS
VERSION : 1.0
VAR_TEMP
    tSeed    : DWORD ;
    tNew     : DWORD ;
END_VAR
BEGIN
NETWORK
TITLE = LCG recurrence X = a*X + c
      L     MD    100        // Load current seed
      L     L#1664525       // Multiplier a
      *D                     // ACCU1 := ACCU2 * a (32-bit, wraps mod 2^32)
      L     DW#16#3D7D41A5  // Increment c
      +D                     // ACCU1 := ACCU1 + c
      T     MD    104        // Save the new pseudo-random DWORD
      T     MD    100        // Roll into seed for next scan
NETWORK
TITLE = Convert to REAL in [0.0, 1.0)
      L     MD    104
      DTR                    // Convert DWORD -> REAL (in MD 200)
      L     4.294967E+09     // 2^32 as IEEE-754 REAL
      /R                     // Scale to [0, 1)
      T     MD    200
END_FUNCTION

Notes on the STL implementation:

  • *D multiplies ACCU1 by ACCU2 as a 32-bit signed integer with implicit wrap-around. Because the lower 32 bits are retained, this is equivalent to mod 232 for the unsigned values used by the LCG.
  • DTR converts a 32-bit integer to IEEE-754 REAL. The result occupies ACCU1; T MD 200 stores it in a Merker DWORD, which is interpreted as REAL on read.
  • Use a static Merker word or a DB for MD 100/104 so the seed survives warm restarts. In OB100, initialize MD 100 with a non-zero seed (for example, the CPU's clock: L SFC1 (READ_CLK); T MD 100) so two consecutive power-ups do not produce identical sequences.

SCL Implementation for S7-SCL-equipped CPUs

If the CPU has the S7-SCL optional package installed (or you program with STEP 7 Professional that bundles SCL), the same algorithm becomes self-documenting and parameterizable:

FUNCTION FC 101 : REAL
TITLE  = Pseudo-random REAL in custom range
VAR_INPUT
    iMin   : REAL :=    0.0;
    iMax   : REAL :=  100.0;
    iSeed  : IN_OUT  DWORD;
END_VAR
VAR_TEMP
    tNew   : DWORD;
    tReal  : REAL;
END_VAR
CONST
    C_MULT  : DWORD := DWORD#16#0019653D;   // 1664525
    C_INCR  : DWORD := DWORD#16#3D7D41A5;   // 1013904223
    C_M2_32 : REAL  := 4.294967296E+09;      // 2^32
END_CONST
BEGIN
    // LCG recurrence
    iSeed := (iSeed * C_MULT) + C_INCR;

    // Scale to [0, 1)
    tReal := DWORD_TO_REAL(iSeed) / C_M2_32;

    // Linear interpolation to [iMin, iMax]
    FC101 := iMin + (iMax - iMin) * tReal;
END_FUNCTION

Calling FC 101 from OB1 (STL syntax):

NETWORK
TITLE = Random value in [0.0, 1000.0]
      L     L#0.000000e+00
      T     MD    300        // iMin = 0.0
      L     L#1.000000e+03
      T     MD    304        // iMax = 1000.0
      CALL  FC   101
       iMin   := MD   300
       iMax   := MD   304
       iSeed  := MD   100
       FC101  := MD   200
Type pitfall. SCL interprets L#1.0e+03 as a 64-bit integer literal. Use the floating-point literal syntax 1.0e+03 (without the L# prefix) or the explicit cast REAL#1000.0 when feeding constants to REAL VAR_INPUTs.

Scaling the Output to Specific Test Ranges

The base FC 100 returns a value uniformly distributed in [0.0, 1.0). Three common scaling patterns used in test harnesses follow.

Target application Range Scaling formula STEP 7 STL fragment
Process variable simulation 0.0 – 100.0 % x · 100.0 L 1.000000e+02
*R
T MD 210
Current with offset 4.0 – 20.0 mA 4.0 + x · 16.0 L 4.0 ; T MD 220
L MD 210 ; L 16.0 ; *R ; +R
T MD 222
Large magnitude (e.g. 0 – 1,000,000) 0 – 1.0E+06 x · 1.0E+06 L 1.000000e+06
*R
T MD 230
Bipolar -500.0 – +500.0 (x · 2.0 - 1.0) · 500.0 see snippet below

Bipolar fragment (inserts in Network 4 after FC 100):

NETWORK
TITLE = Bipolar [-500.0, +500.0]
      L     MD    200        // x in [0,1)
      L     2.000000e+00
      *R                     // x * 2.0
      L     1.000000e+00
      -R                     // (x*2.0) - 1.0  in [-1, +1)
      L     5.000000e+02
      *R                     // * 500.0
      T     MD    240

Generating Extremely Large Floating-Point Values

The IEEE-754 single-precision REAL used by S7-300 has a dynamic range of roughly ±3.4 × 1038 with about 7 decimal digits of precision. The LCG produces a 32-bit unsigned integer. After dividing by 232, the value is in [0, 1). To reach "extremely large" magnitudes requested by the original use case, multiply by a large constant before storing:

NETWORK
TITLE = Large-magnitude test value
      L     MD    200        // uniform in [0, 1)
      L     3.400000e+38    // near REAL max
      *R
      T     MD    250        // overflows to +Inf for results > 3.4e38

      // Optional: saturate to max before storing
      L     MD    250
      ABS
      L     3.400000e+38
      >R                     // if |value| > 3.4e38
      JC    OVFL
      L     MD    250
      JU    STOR
OVFL: L     3.400000e+38
      T     MD    250
STOR: NOP 0
Watch the OV / OS flags. A REAL multiplication that exceeds the IEEE-754 range sets OV (overflow) in the status word. If subsequent code uses JC or JO for branching, the next instruction sequence may branch unexpectedly. Clear the OV bit with CLR or use the saturation logic shown above to keep downstream maths safe.

Commissioning and OB1 Integration

Recommended wiring for a reproducible test rig:

  1. Create a new S7 program in STEP 7 and insert the S7-300 station. The station must match the physical CPU order number (for example, 6ES7 315-2EH14-0AB0 for CPU 315-2 PN/DP, firmware V3.2).
  2. Add FC 100 (or FC 101) to the S7 program / Blocks container.
  3. Allocate Merker bytes MB 100–MB 207 (or a dedicated DB) for the seed and REAL outputs. Verify these addresses do not overlap with the process image or any other function block.
  4. In OB100 (warm restart), call the SFC 1 READ_CLK to seed MD 100 from the CPU clock. This guarantees a different sequence every power-up.
  5. In OB1, place a single call to FC 100 (or FC 101) in Network 1. The call must execute every scan; placing it in OB35 with a 100 ms cycle time reduces CPU load while still producing a fast-changing value for HMI display.
  6. Download the project, switch the CPU to RUN, and monitor MD 200 with the STEP 7 Monitor/Modify tool. The value should change every scan, span the configured range, and show no periodic pattern over a 1000-sample window.

Verification and Statistical Quality Checks

A short list of field checks before signing off the test program:

Test Expected behaviour How to verify
Range endpoints First sample may be near 0 but never negative; last sample never exceeds iMax Trigger 10,000 calls via OB35 and record min/max in a DB
Period 2,147,483,647 distinct values before repetition Insert a collision counter that increments when MD 200 repeats a previous value; expect collisions to begin only after ~2 billion calls
Distribution Mean ≈ 0.5 of range, std-dev ≈ 0.289 of range Sum 1,000 samples, divide by 1,000 in a separate FB, compare with the analytical value
Reproducibility Same seed → identical sequence Force MD 100 to a fixed value, restart CPU, capture first 100 samples; verify against saved reference
No pattern at HMI update Visually random at 250 ms update Display MD 200 on a WinCC flexible / TIA WinCC trend; eye-check for visible bands

Comparison with the S7-1500 Random Instruction

Property S7-300 LCG (this article) S7-1500 / S7-1200 Random
Library origin User-defined FC, standard instructions Extended instructions, "Cryptography" subgroup
Output type REAL via separate conversion (DTR + /R) 32-bit INT (range 0 – 2,147,483,647)
Period 232 Implementation-defined; uses a hardware-based entropy source on S7-1500
Deterministic with seed Yes No (truly non-deterministic)
Error output n/a (user manages overflow) None — the block does not report an error; see official Random documentation
Use cases Test data, simulation, HMI demo Challenge values, unique IDs, non-cryptographic randomness
Firmware requirement Any S7-300 firmware S7-1500 ≥ V1.8 / S7-1200 ≥ V4.2

Limitations and Edge Cases

  • 32-bit wrap only. If you change *D to *R (REAL multiplication), the mod-232 behaviour disappears and the sequence degenerates. Stick to DWORD multiplication on the integer path before scaling to REAL.
  • Negative seeds. Because the S7-300's 32-bit registers are signed in STL but the LCG constants are unsigned, always initialize MD 100 with a positive DWORD (e.g., L DW#16#12345678). Loading L -1 as a 32-bit value gives 0xFFFFFFFF which is fine, but intermediate results read confusingly in monitor view.
  • Cold restart vs. warm restart. Merker is retained across warm restart on S7-300 (non-retentive Merker is lost on power off; retentive Merker survives). Use MB 100–MB 199 as retentive Merker, or store the seed in a DB marked Non-retentive = No.
  • OB35 timing. When OB35 runs at 100 ms, the random value updates 10 times per second. For slower updates (e.g., simulating a slowly drifting process variable) gate the call with a clock-bit such as M0.5 (1 Hz) or a TON preset of 5000 ms.
  • No time jitter. Unlike the S7-1500 Random block, this LCG is purely deterministic. If two S7-300 stations start with the same seed, they will produce identical sequences — useful for reproducible tests but a hazard for security-sensitive applications.

FAQ

Does the S7-300 CPU have a built-in Random instruction like the S7-1500?

No. The S7-1500 / S7-1200 Random instruction lives in the TIA Portal extended instruction library and is not present in STEP 7 V5.5 or in the S7-300 firmware. Implement the generator inside an FC using DWORD arithmetic as documented above. Reference: Random (S7-1500).

Which constants give a full-period LCG on a 32-bit CPU?

Use multiplier a = 1664525 (0x0019653D) and increment c = 1013904223 (0x3D7D41A5) with modulus m = 2^32. These values, from the Numerical Recipes / nrand48 family, deliver a full period of 2^32 values before repetition.

How do I scale the 32-bit value to a floating-point range?

Convert the DWORD to REAL with the DTR instruction, then divide by 4.294967296E+09 (2^32) to obtain a uniform value in [0.0, 1.0). Multiply by (iMax - iMin) and add iMin to land in any custom floating-point range. Use REAL literals (e.g., 1.0e+03) rather than the L# integer prefix when feeding REAL constants.

Why does my multiplication set the OV (overflow) bit?

REAL multiplication that exceeds ~3.4e38 sets OV because the result cannot be represented as IEEE-754 single precision. Either reduce the upper bound, add saturation logic with >R and JC, or clear the status word with CLR after the operation. The LCG itself does not overflow because the DWORD *D operation wraps mod 2^32.

Can I generate the random value inside OB35 instead of OB1?

Yes. Calling FC 100 from OB35 (cyclic interrupt) reduces CPU load because the LCG step and the REAL conversion only run at the OB35 interval (for example, every 100 ms) instead of every OB1 scan. Store the seed in retentive Merker (MB 100–199) or in a retentive DB so the value persists across warm restarts.

Back to blog