Files
Server-scripts/work/compareStartUpScripts.py
T

280 lines
7.8 KiB
Python
Raw Normal View History

2026-04-14 23:55:15 +02:00
import re
import pprint
import re
from collections import defaultdict
# =====================================================================
# Unified DynamicObject (replaces DynamicMObject + DynamicCppObject)
# =====================================================================
class DynamicObject(dict):
"""
A unified dynamic object that:
- Behaves like a dict
- Supports attribute access
- Auto-creates nested fields on demand
"""
def __getattr__(self, name):
if name not in self:
self[name] = DynamicObject()
return self[name]
def __setattr__(self, name, value):
self[name] = value
def ensure_path(self, path_parts):
obj = self
for part in path_parts:
if part not in obj:
obj[part] = DynamicObject()
obj = obj[part]
return obj
def __repr__(self):
return dict.__repr__(self)
# =====================================================================
# MATLAB FRD REG PARSER
# =====================================================================
def parse_oFRDReg_from_mfile(filename):
oFRDReg = DynamicObject()
# Regex to capture: oFRDReg.<path> = <value>;
pattern = re.compile(r"oFRDReg\.([A-Za-z0-9_.]+)\s*=\s*([^;]+);")
with open(filename, "r") as f:
for line in f:
match = pattern.search(line)
if match:
full_path = match.group(1)
value_raw = match.group(2).strip()
# Convert numeric/string values when possible
try:
value = eval(value_raw)
except:
value = value_raw
# Split path: e.g. MSI.vcm_sel → ["MSI", "vcm_sel"]
parts = full_path.split(".")
parts = [ p.upper() for p in parts ]
attr_name = parts[-1]
parent_path = parts[:-1]
# Create nested path
parent = oFRDReg.ensure_path(parent_path)
setattr(parent, attr_name, value)
return oFRDReg
# =====================================================================
# Macro expansion helper
# =====================================================================
def expand_macro_pattern(pattern, **kwargs):
"""
Expand a macro pattern containing tokens like:
CH1##_##address##_OFFSET
into the final register string.
kwargs maps token names → values, e.g. address="CFG".
"""
s = pattern
# Replace tokens inside patterns like address##_XYZ
for key, value in kwargs.items():
s = s.replace(key, value)
# Resolve concatenation '##'
s = s.replace("##", "")
# Clean up double underscores
while "__" in s:
s = s.replace("__", "_")
return s
# =====================================================================
# C MACRO PARSER
# =====================================================================
def remove_cpp_comments(text):
"""
Remove /* block comments */ and // line comments.
"""
# Remove multiline comments
text = re.sub(r"/\*.*?\*/", "", text, flags=re.DOTALL)
# Removedefines
text = re.sub(r"#define.*", "", text)
# Remove single-line comments
text = re.sub(r"//.*", "", text)
return text
def parse_c_macros(filename):
registers = DynamicObject()
# Macro → register expansion patterns
macro_definitions = {
# 3-argument write (slow/fast)
"SLOW_WRITE_FIELD": (
"CH1_##address##",
"CH1_##address##_##field##",
),
"FAST_WRITE_FIELD": (
"CH0_##address##",
"CH0_##address##_##field##",
),
# Read field, 2 arguments
"SLOW_READ_FIELD": (
"CH1_##address##",
"CH1_##address##_##field##",
),
"FAST_READ_FIELD": (
"CH0_##address##",
"CH0_##address##_##field##",
),
# Register read/write (1 or 2 arguments)
"SLOW_READ_REGISTER": (
"CH1_##address##",
),
"FAST_READ_REGISTER": (
"CH0_##address##",
),
"SLOW_WRITE_REGISTER": (
"CH1_##address##",
),
"FAST_WRITE_REGISTER": (
"CH0_##address##",
),
"FAST_WRITE_INHIBIT": (
"CH0##_##address1##_CH0_##address2##",
),
"SLOW_WRITE_INHIBIT": (
"CH1##_##address1##_CH0_##address2##",
)
}
# Generic regex for ALL macro calls (1–4 args)
call_regex = re.compile(
r"([A-Z_]+)\s*\(\s*([A-Za-z0-9_]+)" # FUN and arg1: address1
r"(?:\s*,\s*([A-Za-z0-9_]+))?" # arg2: field, address2
r"(?:\s*,\s*([A-Za-z0-9_+\-*/() ]+))?" # arg3: field, data
r"(?:\s*,\s*([A-Za-z0-9_+\-*/() ]+))?" # arg4: data (expression allowed)
r"\s*\)"
)
with open(filename, "r") as f:
text = f.read()
text = remove_cpp_comments(text)
for match in call_regex.finditer(text):
macro, a1, a2, a3, a4 = match.groups()
text = text.replace(match.group(0), "") # remove after processing
if macro not in macro_definitions or "READ" in macro:
continue
patterns = macro_definitions[macro]
# Build the macro variable map
kwargs = {}
if a4 == None:
if a1: kwargs["address"] = a1
if a2: kwargs["field"] = a2
if a3: kwargs["data"] = a3
else:
if a1: kwargs["address1"] = a1
if a2: kwargs["address2"] = a2
if a3: kwargs["field"] = a3
if a4: kwargs["data"] = a4
# Expand patterns into real register names
expanded = [expand_macro_pattern(p, **kwargs) for p in patterns]
# Store results
if a2 and a3: # WRITE_FIELD
registers[expanded[0].upper()] = {a2.upper(): a3}
else: # REGISTER read/write
registers[expanded[0].upper()] = a3
return registers
def upper_keys_recursive(d):
new_dict = {}
for k, v in d.items():
key_up = str(k).upper()
if isinstance(v, dict):
new_dict[key_up] = upper_keys_recursive(v)
else:
new_dict[key_up] = v
return new_dict
def diff_dicts(d1, d2, path="",dictnames = ["M","CPP"]):
"""
Recursively compare two dictionaries and print differences.
Shows:
- Keys only in d1
- Keys only in d2
- Keys in both where values differ
"""
# Keys present in both dictionaries
common_keys = d1.keys() & d2.keys()
# Keys only in first dict
only_d1 = d1.keys() - d2.keys()
for key in sorted(only_d1):
print(f"{path}{key}: only in {dictnames[0]}")
# Keys only in second dict
only_d2 = d2.keys() - d1.keys()
for key in sorted(only_d2):
print(f"{path}{key}: only in {dictnames[1]}")
# Compare common keys
for key in sorted(common_keys):
v1 = d1[key]
v2 = d2[key]
new_path = f"{path}{key}."
# If both values are dictionaries → recurse
if isinstance(v1, dict) and isinstance(v2, dict):
diff_dicts(v1, v2, new_path)
continue
# If values differ → print
if v1 != v2:
print(f"{path}{key}:")
print(f" {dictnames[0]} = {v1}")
print(f" {dictnames[1]} = {v2}")
# =====================================================================
# Example usage
# =====================================================================
# Example usage:
if __name__ == "__main__":
obj = parse_oFRDReg_from_mfile("startTau_generic_Generated.m")
pprint.pprint(obj)
regs = parse_c_macros("/home/lucas/git/mems_stable/trixel3_software/tic/source/trixel3_tic_mems_model_lbd001rrxab11.cpp")
pprint.pprint(regs)
diff_dicts(regs,obj)
print("Fin")