FUNCTION "LGF_CalcCRC32Advanced_MCopy" : DWord
TITLE = LGF_CalcCRC32Advanced
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : Siemens_Industry_Support
FAMILY : LGF
NAME : LGF_CRC32Advanced
//Die CRC-Berechnung wird für die Fehlererkennung bei der Datenübertragung verwendet. Das Ergebnis einer Berechnung liefert einen CRC-Wert über die gesendeten Daten. Der Empfänger erkennt eine fehlerhafte Übertragung aufgrund des ungleichen CRC-Werts. Die Funktion `LGF_CalcCRC32Advanced` verwendet als Generatorpolynom (Maske) 32 Bit sowie die Parameter `finalXorValue`, `reflectInput` und `reflectResult`.
   VAR_INPUT 
      initValue { S7_PredefinedAssignment := '16#00'} : DWord;   // Startwert, mit dem die Berechnung durchgeführt wird.  Wenn keinen Startwert notwendig ist - 16#00 zuweisen.
      mask { S7_PredefinedAssignment := '16#00'} : DWord;   // Generatorpolynom, mit dem die Berechnung durchgeführt wird.  (Maske / CRC Polynom)
      finalXorValue { S7_PredefinedAssignment := '16#00'} : DWord;   // Wert, mit dem am Ende eine weitere XOR-Operation durchgeführt wird
      nInLowerLimit : Int;
      nInUpperLimit : Int;
      reflectInput { S7_PredefinedAssignment := 'FALSE'} : Bool;   // TRUE: wird die Reihenfolge der Bits innerhalb des Eingangsbytes gespiegelt. Aus der Reihenfolge 0…7 wird 7…0. 
      reflectResult { S7_PredefinedAssignment := 'FALSE'} : Bool;   // TRUE: wird die Reihenfolge der Bits innerhalb des Ergebnisses gespiegelt. Aus der Reihenfolge 0…7 wird 7…0.
   END_VAR

   VAR_IN_OUT 
      "array" : Array[*] of Byte;   // Datenstrom, für den der CRC-Wert berechnet werden soll.
   END_VAR

   VAR_TEMP 
      tempInput : Byte;   // Temp aktuelles eingangs Byte
      tempCRC : DWord;   // Temp CRC Prüfsumme
      tempInvCRC : DWord;   // Temp invertierte CRC Prüfsumme
      tempLowerLimit : DInt;   // Temp Untergrenze Eingangs Array
      tempUpperLimit : DInt;   // Temp Obergrenze Eingangs Array
      tempIndexArray : DInt;   // Temp Array schleifen Index
      tempIndexCRC : DInt;   // Temp CRC Schleifen Index
   END_VAR

   VAR CONSTANT 
      SHIFT_ONE_BIT : UInt := 1;   // Schieben der CRC Berechnung um ein Bit
      SHIFT_THREE_BYTE : UInt := 24;   // Schieben um drei Byte (= 24 Bit)
      ARRAY_FIRST_DIMENSION : UInt := 1;   // Erste Dimension im Array
      CRC_LOOP_LOWER_LIMIT : DInt := 0;   // Untere Grenze für die CRC Berechnungsschleife
      CRC_LOOP_UPPER_LIMIT : DInt := 7;   // Obere Grenze für die CRC Berechnungsschleife
   END_VAR


BEGIN
	REGION Block info header
	  //===============================================================================
	  // SIEMENS AG / (c)Copyright 2019
	  //-------------------------------------------------------------------------------
	  // Title:            LGF_CRC32Advanced
	  // Comment/Function: This function makes a CRC calculation according
	  //                   to the CRC-32 algorithm
	  //                   advanced because of reflect In/Out and final XOR combination
	  // Library/Family:   LGF (Library General Functions)
	  // Author:           Siemens Digital Industry Support
	  // Tested with:      CPU 1515F-2 PN FW:V2.6
	  // Engineering:      TIA Portal V15.1 Update 2
	  // Restrictions:     ENO disabled - no error handling, no failure possible
	  // Requirements:     PLC (S7-1200 / S7-1500)
	  //-------------------------------------------------------------------------------
	  // Change log table:
	  // Version  | Date       | Expert in charge       | Changes applied
	  //----------|------------|------------------------|------------------------------
	  // 01.00.00 03.07.2018  Siemens Industry Online Support
	  //                      First released version
	  // 01.00.01 17.08.2018  Siemens Industry Online Support
	  //                      Upgrade: TIA V15 Update 2
	  // 01.00.02 23.11.2018  Siemens Industry Online Support
	  //                      Upgrade: TIA V15.1
	  // 01.00.03 09.10.2019  Simatic Systems Support
	  //                      Code refactoring, comments added
	  // 03.00.00 23.04.2020  Simatic Systems Support
	  //                      Set version to V3.0.0, harmonize the version of the whole library
	  // 03.00.01 12.11.2020  Simatic Systems Support
	  //                      Insert documentation
	  //                      Assign default start values to optional inputs - `initValue`, `mask`, `finalXorValue`, `reflectInput`, `reflectResult`
	  //=============================================================================
	END_REGION Block info header
	
	REGION DESCRIPTION
	  (/*
	Die Funktion berechnet aus einem beliebig großen Datenstrom den CRC-Wert. Der Datenstrom setzt sich aus den einzelnen Elementen des Arrays am Ein- / Ausgangsparameter `array` zusammen. Der Startwert `initValue` und das Generatorpolynom `mask` sind frei wählbar.
	
	Über die booleschen Eingangsparameter `reflectInput` und `reflectResult` können Sie optional die Bits der Eingangsdaten bzw. des CRC-Werts spiegeln. Außerdem wird mit dem CRC-Wert am Ende und dem Wert `finalXorValue` eine XOR-Operation durchgeführt.
	
	Hinweis
	:    Für die Berechnung der CRC-Werte stehen verschiedene Online-Tools zur Verfügung. Die Funktion des Bausteins wurde mit folgendem Online-Tool getestet, da es die Eingangsparameter `mask` (Polynom) und `initValue` (Initial Value) unterstützt:
	:    http://www.sunshine2k.de/coding/javascript/crc/crc_js.html
	*/)
	END_REGION DESCRIPTION
	
	REGION Initialization and input data processing
	  // Input array size calculation
	    #tempLowerLimit := #nInLowerLimit;//LOWER_BOUND(ARR := #array, DIM := #ARRAY_FIRST_DIMENSION);
	  #tempUpperLimit := #nInUpperLimit;//UPPER_BOUND(ARR := #array, DIM := #ARRAY_FIRST_DIMENSION);
	  // Defining initial value for algorithm
	  #tempCRC := #initValue;
	END_REGION
	
	REGION CRC calculation
	  FOR #tempIndexArray := #tempLowerLimit TO #tempUpperLimit DO
	    
	    IF #reflectInput THEN
	      // Reverse bit order in input byte
	      #tempInput.%X7 := #array[#tempIndexArray].%X0;
	      #tempInput.%X6 := #array[#tempIndexArray].%X1;
	      #tempInput.%X5 := #array[#tempIndexArray].%X2;
	      #tempInput.%X4 := #array[#tempIndexArray].%X3;
	      #tempInput.%X3 := #array[#tempIndexArray].%X4;
	      #tempInput.%X2 := #array[#tempIndexArray].%X5;
	      #tempInput.%X1 := #array[#tempIndexArray].%X6;
	      #tempInput.%X0 := #array[#tempIndexArray].%X7;
	    ELSE
	      #tempInput := #array[#tempIndexArray];
	    END_IF;
	    
	    // Perform division using XOR function for appropriate DWord in array (with shift byte into MSB of 32bit CRC)
	    #tempCRC := #tempCRC XOR SHL(IN := BYTE_TO_DWORD(#tempInput), N := #SHIFT_THREE_BYTE);
	    
	    FOR #tempIndexCRC := #CRC_LOOP_LOWER_LIMIT TO #CRC_LOOP_UPPER_LIMIT DO
	      // Check if MSB is set
	      IF #tempCRC.%X31 THEN
	        // Shift left and perform division by mask polynomial using XOR function
	        #tempCRC := SHL(IN := #tempCRC, N := #SHIFT_ONE_BIT) XOR #mask;
	      ELSE
	        // Shift left without division
	        #tempCRC := SHL(IN := #tempCRC, N := #SHIFT_ONE_BIT);
	      END_IF;
	    END_FOR;
	    
	  END_FOR;
	END_REGION
	
	REGION Outputs assignment
	  IF #reflectResult THEN
	    // Reverse bit order in CRC checksum
	    #tempInvCRC.%X31 := #tempCRC.%X0;
	    #tempInvCRC.%X30 := #tempCRC.%X1;
	    #tempInvCRC.%X29 := #tempCRC.%X2;
	    #tempInvCRC.%X28 := #tempCRC.%X3;
	    #tempInvCRC.%X27 := #tempCRC.%X4;
	    #tempInvCRC.%X26 := #tempCRC.%X5;
	    #tempInvCRC.%X25 := #tempCRC.%X6;
	    #tempInvCRC.%X24 := #tempCRC.%X7;
	    #tempInvCRC.%X23 := #tempCRC.%X8;
	    #tempInvCRC.%X22 := #tempCRC.%X9;
	    #tempInvCRC.%X21 := #tempCRC.%X10;
	    #tempInvCRC.%X20 := #tempCRC.%X11;
	    #tempInvCRC.%X19 := #tempCRC.%X12;
	    #tempInvCRC.%X18 := #tempCRC.%X13;
	    #tempInvCRC.%X17 := #tempCRC.%X14;
	    #tempInvCRC.%X16 := #tempCRC.%X15;
	    #tempInvCRC.%X15 := #tempCRC.%X16;
	    #tempInvCRC.%X14 := #tempCRC.%X17;
	    #tempInvCRC.%X13 := #tempCRC.%X18;
	    #tempInvCRC.%X12 := #tempCRC.%X19;
	    #tempInvCRC.%X11 := #tempCRC.%X20;
	    #tempInvCRC.%X10 := #tempCRC.%X21;
	    #tempInvCRC.%X9 := #tempCRC.%X22;
	    #tempInvCRC.%X8 := #tempCRC.%X23;
	    #tempInvCRC.%X7 := #tempCRC.%X24;
	    #tempInvCRC.%X6 := #tempCRC.%X25;
	    #tempInvCRC.%X5 := #tempCRC.%X26;
	    #tempInvCRC.%X4 := #tempCRC.%X27;
	    #tempInvCRC.%X3 := #tempCRC.%X28;
	    #tempInvCRC.%X2 := #tempCRC.%X29;
	    #tempInvCRC.%X1 := #tempCRC.%X30;
	    #tempInvCRC.%X0 := #tempCRC.%X31;
	    
	    #LGF_CalcCRC32Advanced_MCopy := #tempInvCRC XOR #finalXorValue;
	  ELSE
	    #LGF_CalcCRC32Advanced_MCopy := #tempCRC XOR #finalXorValue;
	  END_IF;
	  // no error handling needed
	  ENO := TRUE;
	END_REGION
	
END_FUNCTION

