Source code for m2isar.backends.trace_gen.tracemodel.MetaTraceModel

# 
# Copyright 2025 Chair of EDA, Technical University of Munich
# 
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# 
#       http://www.apache.org/licenses/LICENSE-2.0
# 
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

import re

[docs] class MetaTraceModel_base:
[docs] __isFrozen = False
[docs] def __setattr__(self, key, value): if self.__isFrozen and not hasattr(self, key): raise TypeError("Attempting to add new attribute to frozen class %r" %self) object.__setattr__(self, key, value)
def __init__(self): self.__isFrozen = True
[docs] class Trace(MetaTraceModel_base): def __init__(self, name_, core_):
[docs] self.name = name_
[docs] self.core = core_
[docs] self.instructionGroups = []
[docs] self.traceValues = {}
[docs] self.separator = "|"
super().__init__()
[docs] def createAndAddTraceValue(self, name_, type_="int", size_=-1): trVal = TraceValue(name_, type_, size_) self.traceValues[name_] = trVal return trVal
[docs] def createAndAddInstructionGroup(self, name_, id_): instrType = InstructionGroup(name_, id_, self) self.instructionGroups.append(instrType) return instrType
[docs] def getAllTraceValues(self): return self.traceValues.values()
[docs] def getAllInstructionGroups(self): return self.instructionGroups
[docs] def getAllMappings(self): mappings = [] for instrType_i in self.getAllInstructionGroups(): mappings.extend(instrType_i.getAllMappings()) return mappings
[docs] def getAllDescriptions(self): descriptions = [] for map_i in self.getAllMappings(): descriptions.append(map_i.description) return descriptions
[docs] def setSeparator(self, sep_): self.separator = sep_
[docs] def getSeparator(self): return self.separator
[docs] class InstructionGroup(MetaTraceModel_base): def __init__(self, name_, id_, parent_):
[docs] self.name = name_
[docs] self.identifier = id_
[docs] self.instructions = []
[docs] self.bitfields = []
[docs] self.mappings = {}
[docs] self.__parent = parent_
super().__init__()
[docs] def addInstruction(self, name_): self.instructions.append(name_)
[docs] def addBitfield(self, name_): self.bitfields.append(name_)
[docs] def createAndAddMapping(self, trValName_, description_, position_): # Look up trace-value in dict. of parent/trace-model try: trVal = self.__parent.traceValues[trValName_] except KeyError: raise TypeError("Mapping for instruction %s: Cannot create mapping for trace-value %s. Trace-value does not exist (Make sure to add all trace-values to the trace-model before creating mappings)" %(self.name, trValName_)) mapping = Mapping(self, trVal, description_, position_) self.mappings[trValName_] = mapping return mapping
[docs] def getAllInstructions(self): return self.instructions
[docs] def getAllBitfields(self): return self.bitfields
[docs] def getAllMappings(self): return self.mappings.values()
[docs] def getMapping(self, trVal_): try: return self.mappings[trVal_.name] except KeyError: return None
[docs] def getAllPreMappings(self): mappings = [] for map_i in self.mappings: map_i = self.mappings[map_i] if map_i.positionIsPre(): mappings.append(map_i) return mappings
[docs] def getAllPostMappings(self): mappings = [] for map_i in self.mappings: map_i = self.mappings[map_i] if map_i.positionIsPost(): mappings.append(map_i) return mappings
[docs] class TraceValue(MetaTraceModel_base): def __init__(self, name_, type_, size_):
[docs] self.name = name_
[docs] self.dataType = type_
[docs] self.size = size_
super().__init__()
[docs] class Mapping(MetaTraceModel_base): def __init__(self, type_, trVal_, descr_, pos_):
[docs] self.instructionGroup = type_
[docs] self.traceValue = trVal_
# self.description = Description(self, descr_)
[docs] self.description = DescriptionParser().parse_description_string(descr_, self.instructionGroup)
if pos_ not in ["pre", "post"]: raise RuntimeError("Cannot create object of type MetaTraceModel::Mapping with position \"%s\"! Currently supported positions are \"pre\" and \"post\"" %pos_)
[docs] self.position = pos_
super().__init__()
[docs] def positionIsPre(self): return (self.position == "pre")
[docs] def positionIsPost(self): return (self.position == "post")
[docs] def getTraceValue(self): return self.traceValue
[docs] def getDescription(self): return self.description
[docs] def getInstructionGroup(self): return self.instructionGroup
[docs] class Description(MetaTraceModel_base): def __init__(self, type_, value, resolved=False, nested_descriptions=None):
[docs] self.type = type_
[docs] self.value = value
[docs] self.resolved = resolved
[docs] self.nested_descriptions = nested_descriptions or []
[docs] def getDescriptionType(self): return self.type
[docs] def getDescriptionValue(self): return self.value
[docs] def getNestedDescriptions(self): return self.nested_descriptions
[docs] def __repr__(self): if self.nested_descriptions: nested_repr = ', '.join([repr(nd) for nd in self.nested_descriptions]) return f"Description(type={self.type}, value={self.value}, resolved={self.resolved} , nested_descriptions=[{nested_repr}])" else: return f"Description(type={self.type}, value={self.value}, resolved={self.resolved})"
[docs] class DescriptionParser(MetaTraceModel_base):
[docs] def parse_description_string(self, desc_string, instructionGroup, resolved = False): parsed_descriptions = [] resolved = resolved buffer = "" i = 0 while i < len(desc_string): if desc_string[i:i+3] == "$pc": if buffer: parsed_descriptions.append(Description(type_="string", value=buffer, resolved=resolved)) buffer = "" parsed_descriptions.append(Description(type_="pc", value="pc", resolved=resolved)) i += 3 elif desc_string[i:i+4] == "$asm": if buffer: parsed_descriptions.append(Description(type_="string", value=buffer, resolved=resolved)) buffer = "" parsed_descriptions.append(Description(type_="asm", value="asm", resolved=resolved)) i += 4 elif desc_string[i:i+5] == "$code": if buffer: parsed_descriptions.append(Description(type_="string", value=buffer, resolved=resolved)) buffer = "" parsed_descriptions.append(Description(type_="code", value="code", resolved=resolved)) i += 5 elif desc_string[i:i+5] == "$reg{": if buffer: parsed_descriptions.append(Description(type_="string", value=buffer, resolved=resolved)) buffer = "" i += 5 nested_content, i = self.extract_nested_content(desc_string, i) parsed_descriptions.append(Description(type_="reg", value="reg", resolved=resolved, nested_descriptions=self.parse_description_string(nested_content, instructionGroup, resolved=resolved))) elif desc_string[i:i+5] == "$csr{": if buffer: parsed_descriptions.append(Description(type_="string", value=buffer, resolved=resolved)) buffer = "" i += 5 nested_content, i = self.extract_nested_content(desc_string, i) parsed_descriptions.append(Description(type_="csr", value="csr", resolved=resolved, nested_descriptions=self.parse_description_string(nested_content, instructionGroup, resolved=resolved))) elif desc_string[i:i+10] == "$bitfield{": if buffer: parsed_descriptions.append(Description(type_="string", value=buffer, resolved=resolved)) buffer = "" i += 10 nested_content, i = self.extract_nested_content(desc_string, i, single_level=True) if nested_content not in instructionGroup.bitfields: instructionGroup.addBitfield(nested_content) parsed_descriptions.append(Description(type_="bitfield", value=nested_content, resolved=resolved)) elif desc_string[i:i+10] == "$resolved{": if buffer: parsed_descriptions.append(Description(type_="string", value=buffer, resolved=resolved)) buffer = "" i += 10 nested_content, i = self.extract_nested_content(desc_string, i) parsed_descriptions.extend(self.parse_description_string(nested_content, instructionGroup, resolved=True)) else: buffer += desc_string[i] i += 1 if buffer: parsed_descriptions.append(Description(type_="string", value=buffer, resolved=resolved)) return parsed_descriptions
[docs] def extract_nested_content(self, desc_string, start_idx, single_level=False): nested_content = "" open_braces = 1 i = start_idx while i < len(desc_string) and open_braces > 0: if desc_string[i] == '{' and not single_level: open_braces += 1 elif desc_string[i] == '}': open_braces -= 1 if open_braces == 0: break nested_content += desc_string[i] i += 1 return nested_content, i + 1