Memory Protection
Cyclic Redundancy Check (CRC) is an error-detecting code used to verify the integrity of digital data. This feature ensures configuration consistency and integrity across different drives. When the same configuration file is applied to multiple drives and then stored in each device, the resulting CRC value must be identical. This register can be read via the 0x26D1 - CRC output motion core register. The drive calculates this check based on the values of all the user-accessible, motion core parameters that can be stored in non-volatile memory.
In short: a single configuration file yields the same post-store CRC on any supported drive, enabling deterministic verification and seamless portability.
Consistency across drives: the same configuration produces the same stored CRC on any supported drive.
Integrity verification: CRC provides a deterministic post-store integrity check.
Procedure
The CRC is computed once at startup of the drive. When the same configuration file (.xcf file generated by ML3) is loaded onto different drives and a Store All is executed, the resulting CRC calculation is identical across drives.
Verifying the stored configuration’s CRC requires a power cycle after a Store All.
There are two different procedures because the configuration data (set of storable registers) differs depending on the interface in use. To ensure accuracy and proper functionality, please follow the detailed instructions corresponding to your specific interface as outlined below
EtherCAT
Load the .xcf generated by ML3.
Execute Store All.
Power cycle the drive.
Read and verify the post-store CRC.
Summit Safe Series drives also implement CRC. However, even though the safe registers are storable, they are excluded from the Motion Core CRC computation.
CANopen
Load the .xcf generated by ML3.
Execute Store All.
ML3 Store All only stores General and Motion parameters. Make sure to store also Ethernet configuration (IP address, Netmask, Gateway address and MAC address) → Changing Ethernet configuration in ML3
Power cycle the drive.
Read and verify the post-store CRC.
Offline calculation
To validate that the configuration CRC is computed correctly, you can run an offline script that takes the ML3-generated .xcf file as input and reproduces the CRC calculation.
XCF file order
The CRC is computed using the storable registers sorted in ascending order by their index. Since the registers within the .xcf file are not stored in this sequence, they must be reordered prior to performing the offline CRC calculation.
At this stage, and taking into account the storable registers currently implemented, the resulting modifications would be as follows:
CANopen variant
Only the XCF obtained when connected trough CAN (not Ethernet) is valid for this procedure.
The following entries, usually placed at the beginning of the .xcf file:
<Register access="rw" dtype="u8" id="CIA301_COMMS_CAN_NODE_ID_BIT0_IN" subnode="0" storage="0"/>
<Register access="rw" dtype="u8" id="CIA301_COMMS_CAN_NODE_ID_BIT1_IN" subnode="0" storage="0"/>
<Register access="rw" dtype="u8" id="CIA301_COMMS_CAN_NODE_ID_BIT2_IN" subnode="0" storage="0"/>
<Register access="rw" dtype="u8" id="CIA301_COMMS_CAN_NODE_ID_BIT3_IN" subnode="0" storage="0"/>
<Register access="rw" dtype="u32" id="COMMS_ETH_IP" subnode="0" storage="3232236054"/>
<Register access="rw" dtype="u32" id="COMMS_ETH_NET_MASK" subnode="0" storage="4294967040"/>
<Register access="rw" dtype="u32" id="COMMS_ETH_GW" subnode="0" storage="3232236033"/>
<Register access="rw" dtype="u64" id="COMMS_ETH_MAC" subnode="0" storage="80600547656193"/>
<Register access="rw" dtype="s16" id="ERROR_COMMS_CAN_DISCONNECT_OPTION" subnode="0" storage="1"/>
Must be placed at the end of the file after the following entry:
<Register access="rw" dtype="u32" id="MOT_BRAKE_FREQ" subnode="1" storage="10000"/>
EtherCAT variant
The following entry, usually placed at the beginning of the .xcf file:
<Register access="rw" dtype="s16" id="ERROR_COMMS_CAN_DISCONNECT_OPTION" subnode="0" storage="1"/>
Must be placed after the following entry:
<Register access="rw" dtype="u32" id="MOT_BRAKE_FREQ" subnode="1" storage="10000"/>
In Safe drives this should be placed after MOT_BRAKE_FREQ entry and before safety entries, usually starting with FSOE_SAFE_INPUTS_MAP
CSV file order
Because the registers in the .csv file are not stored in the required sequence, they must be reordered before performing the offline CRC calculation. The index is the first field in each comma‑separated entry and the value is the third one.
Python Script
import argparse
import xml.etree.ElementTree as ET
CRC_POLY_CCITT = 0x1021
VALUE = "storage"
SIZE = "dtype"
# Initialize global CRC lookup table
_crc_tabccitt = None
crc = 0
class CRCcompute:
"""Parses data from command line and calls the required renderers"""
def __init__(self):
"""Constructor."""
self.parser = argparse.ArgumentParser()
self.parser.add_argument(
"in_file", type=str, help="File path to a XCF containing the register list"
)
def parse_args(self, args=None):
"""Parses the input arguments."""
self.args = self.parser.parse_args(args)
def run(self):
"""Main execution method."""
xml_tree = ET.parse(self.args.in_file).getroot()
# Extract PartNumber from Device element
device = xml_tree.find(".//Device")
part_number = (
device.attrib.get("PartNumber", "Unknown")
if device is not None
else "Unknown"
)
# Get the filename from the input file path
import os
config_file_name = os.path.basename(self.args.in_file)
# Print the message
print(
f"Calculating configuration CRC for {part_number} using {config_file_name} configuration file"
)
print() # Empty line
# Navigate the nested structure to find Register elements
registers = xml_tree.findall(".//Register")
for idx, register in enumerate(registers):
# Get the register ID
reg_id = register.attrib.get("id", "")
# Stop processing if we encounter a register with ID starting with "FSOE_"
if reg_id.startswith("FSOE_"):
print(f"Stopped at register: {reg_id} (excluding Safe registers)")
print() # Empty line
break
value = register.attrib[VALUE]
size = register.attrib[SIZE]
# Determine number of bytes from datatype
size_map = {
"u8": 1,
"s16": 2,
"u16": 2,
"float": 4,
"u32": 4,
"s32": 4,
"u64": 8,
"s64": 8,
}
num_bytes = size_map.get(size, 2) # Default to 2 bytes if unknown
# Parse value based on datatype
if size == "float":
import struct
# Convert float string to float, then to bytes
float_value = float(value)
reg_bytes = struct.pack(">f", float_value) # big-endian float
else:
# For integer types, check if hex or decimal
if value.startswith("0x"):
int_value = int(value, 16)
else:
int_value = int(value)
# Convert to bytes (big-endian)
reg_bytes = int_value.to_bytes(
num_bytes, byteorder="big", signed=(size.startswith("s"))
)
# Reverse byte order for CRC calculation (little-endian)
reg_bytes_reversed = bytes(reversed(reg_bytes))
# Update CRC with the byte array
out = self.__update_crc(reg_bytes_reversed, size)
print(f"CRC: 0x{out:04X} (decimal: {out})")
def __init_crcccitt_tab(self):
"""
Initialize the CRC-CCITT lookup table.
For optimal performance, the CRC-CCITT calculation uses a lookup table
with pre-compiled values that can be directly applied in the XOR action.
"""
global _crc_tabccitt
_crc_tabccitt = []
for i in range(256):
crc = 0
c = i << 8
for j in range(8):
if (crc ^ c) & 0x8000:
crc = (crc << 1) ^ CRC_POLY_CCITT
else:
crc = crc << 1
c = c << 1
_crc_tabccitt.append(crc & 0xFFFF)
def __update_crc(self, input_data, size_data):
"""
Generic implementation of the CCITT CRC algorithm.
Args:
input_data: bytes or bytearray to calculate CRC for
start_value: initial CRC value (uint16)
Returns:
uint16 CRC value
"""
global _crc_tabccitt
global crc
if _crc_tabccitt is None:
self.__init_crcccitt_tab()
for byte in input_data:
crc = ((crc << 8) ^ _crc_tabccitt[((crc >> 8) ^ byte) & 0x00FF]) & 0xFFFF
return crc
if __name__ == "__main__":
app = CRCcompute()
app.parse_args()
app.run()