vivohost
This commit is contained in:
1 parent
7db38ae9f4
commit
24736d0577
4 files changed
+1160
No files matched your search
@@ -0,0 +1,280 @@
|
||||
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")
|
||||
Reference in new issue
Block a user