[#73220] add parsing and comparing SAIF instance tree data to driver.py
This commit is contained in:
parent
0c0d98d4a6
commit
40f182a3f6
|
|
@ -72,17 +72,22 @@ class staticproperty(property):
|
|||
|
||||
|
||||
class SAIFSignalBit:
|
||||
last_val: int
|
||||
high_time: int
|
||||
low_time: int
|
||||
transitions: int
|
||||
|
||||
def __init__(self):
|
||||
self.last_val = 0
|
||||
self.high_time = 0
|
||||
self.low_time = 0
|
||||
self.transitions = 0
|
||||
|
||||
def aggregate(self, dt, new_val):
|
||||
if int(new_val) != int(self.last_val):
|
||||
def aggregate(self, dt: int, new_val: int):
|
||||
if new_val != self.last_val:
|
||||
self.transitions += 1
|
||||
|
||||
if self.last_val:
|
||||
if self.last_val == 1:
|
||||
self.high_time += dt
|
||||
|
||||
self.last_val = new_val
|
||||
|
|
@ -97,70 +102,17 @@ class SAIFSignal:
|
|||
for _ in range(self.width):
|
||||
self.bits.append(SAIFSignalBit())
|
||||
|
||||
|
||||
class VCDToSAIFParser:
|
||||
def __init__(self):
|
||||
self.signal_definitions = {}
|
||||
self.signal_indirections = {}
|
||||
self.current_time = 0
|
||||
|
||||
def parse(self, vcd_filename):
|
||||
with open(vcd_filename, 'r') as vcd_file:
|
||||
for line in vcd_file:
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
|
||||
match = re.match(r'\$var\s+(\w+)\s+(\d+)\s+(\S+)\s+(\S+)\s*(\S*)\s*\$end', line)
|
||||
if match:
|
||||
_, signal_width, signal_id, signal_name, _ = match.groups()
|
||||
if signal_id in self.signal_indirections:
|
||||
continue
|
||||
|
||||
self.signal_indirections[signal_id] = signal_name
|
||||
self.signal_definitions[signal_name] = SAIFSignal(signal_name, int(signal_width))
|
||||
continue
|
||||
|
||||
match = re.match(r'\#(\d+)', line)
|
||||
if match:
|
||||
timestamp = match.groups()
|
||||
self.current_time = int(timestamp[0])
|
||||
continue
|
||||
|
||||
match = re.match(r'b(\d+)\s+(\S+)', line)
|
||||
if match:
|
||||
value, signal_id = match.groups()
|
||||
|
||||
dt = self.current_time - self.signal_definitions[self.signal_indirections[signal_id]].last_time
|
||||
self.signal_definitions[self.signal_indirections[signal_id]].last_time = self.current_time
|
||||
|
||||
index = 0
|
||||
for bit in reversed(value):
|
||||
self.signal_definitions[self.signal_indirections[signal_id]].bits[index].aggregate(dt, int(bit))
|
||||
index += 1
|
||||
|
||||
continue
|
||||
|
||||
match = re.match(r'(\d+)(\S+)', line)
|
||||
if match:
|
||||
value, signal_id = match.groups()
|
||||
|
||||
dt = self.current_time - self.signal_definitions[self.signal_indirections[signal_id]].last_time
|
||||
self.signal_definitions[self.signal_indirections[signal_id]].last_time = self.current_time
|
||||
|
||||
self.signal_definitions[self.signal_indirections[signal_id]].bits[0].aggregate(dt, int(value))
|
||||
|
||||
for _, signal in self.signal_definitions.items():
|
||||
for i in range(signal.width):
|
||||
if signal.bits[i].last_val == 1:
|
||||
signal.bits[i].high_time += self.current_time - signal.last_time
|
||||
|
||||
signal.bits[i].low_time = self.current_time - signal.bits[i].high_time
|
||||
|
||||
class SAIFInstance:
|
||||
def __init__(self, scope_name):
|
||||
self.scope_name = scope_name
|
||||
self.parent_instance = None
|
||||
self.nets = {}
|
||||
self.child_instances = {}
|
||||
|
||||
class SAIFParser:
|
||||
def __init__(self):
|
||||
self.signal_definitions = {}
|
||||
self.top_instance = None
|
||||
self.current_instance = None
|
||||
|
||||
def parse(self, saif_filename):
|
||||
with open(saif_filename, 'r') as saif_file:
|
||||
|
|
@ -169,6 +121,21 @@ class SAIFParser:
|
|||
if not line:
|
||||
continue
|
||||
|
||||
match = re.search(r'INSTANCE\s+(\w*)', line)
|
||||
if match:
|
||||
instance_name = match.groups()[0]
|
||||
|
||||
instance = SAIFInstance(instance_name)
|
||||
|
||||
if instance_name == "top":
|
||||
self.top_instance = instance
|
||||
else:
|
||||
self.current_instance.child_instances[instance_name] = instance
|
||||
|
||||
instance.parent_instance = self.current_instance
|
||||
self.current_instance = instance
|
||||
|
||||
line = line.replace('\\', '')
|
||||
match = re.search(r'(\w+)\[*(\d*)\]*\s+(\(T(.+)\))+', line)
|
||||
if match:
|
||||
signal_name, bit_index, bit_values, _ = match.groups()
|
||||
|
|
@ -176,31 +143,38 @@ class SAIFParser:
|
|||
if bit_index == '':
|
||||
bit_index = 0
|
||||
|
||||
if signal_name not in self.signal_definitions:
|
||||
self.signal_definitions[signal_name] = SAIFSignal(signal_name)
|
||||
if signal_name not in self.current_instance.nets:
|
||||
signal = SAIFSignal(signal_name)
|
||||
self.current_instance.nets[signal_name] = signal
|
||||
|
||||
for _ in range(0, int(bit_index) - self.signal_definitions[signal_name].width + 1):
|
||||
self.signal_definitions[signal_name].bits.append(SAIFSignalBit())
|
||||
current_signal = self.current_instance.nets[signal_name]
|
||||
|
||||
for _ in range(0, int(bit_index) - current_signal.width + 1):
|
||||
current_signal.bits.append(SAIFSignalBit())
|
||||
|
||||
self.signal_definitions[signal_name].width = int(bit_index) + 1
|
||||
current_signal.width = int(bit_index) + 1
|
||||
|
||||
match = re.search(r'T0 (\d+)', bit_values)
|
||||
if match:
|
||||
low_time = match.groups()[0]
|
||||
|
||||
self.signal_definitions[signal_name].bits[int(bit_index)].low_time = low_time
|
||||
current_signal.bits[int(bit_index)].low_time = int(low_time)
|
||||
|
||||
match = re.search(r'T1 (\d+)', bit_values)
|
||||
if match:
|
||||
high_time = match.groups()[0]
|
||||
|
||||
self.signal_definitions[signal_name].bits[int(bit_index)].high_time = high_time
|
||||
current_signal.bits[int(bit_index)].high_time = int(high_time)
|
||||
|
||||
match = re.search(r'TC (\d+)', bit_values)
|
||||
if match:
|
||||
toggle_count = match.groups()[0]
|
||||
|
||||
self.signal_definitions[signal_name].bits[int(bit_index)].transitions = toggle_count
|
||||
|
||||
current_signal.bits[int(bit_index)].transitions = int(toggle_count)
|
||||
|
||||
match = re.match(r'\s+\)\s+', line)
|
||||
if match and self.current_instance != self.top_instance:
|
||||
self.current_instance = self.current_instance.parent_instance
|
||||
|
||||
|
||||
#######################################################################
|
||||
|
|
@ -2504,38 +2478,42 @@ class VlTest:
|
|||
self.fst2vcd(fn1, tmp)
|
||||
self.vcd_identical(tmp, fn2)
|
||||
|
||||
def compare_saif_contents(self, first, second):
|
||||
# Golden SAIF contents object should have all bits declared, even if zero transitions
|
||||
for signal_name, signal in first.signal_definitions.items():
|
||||
tested_object_signal = second.signal_definitions[signal_name]
|
||||
def compare_saif_instances(self, first: SAIFInstance, second: SAIFInstance):
|
||||
for signal_name, signal in first.nets.items():
|
||||
if signal_name not in second.nets:
|
||||
self.error(f"Signal {signal_name} doesn't exist in the second object\n")
|
||||
|
||||
for instance_name, instance in first.child_instances.items():
|
||||
if instance_name not in second.child_instances:
|
||||
self.error(f"Instance {instance_name} doesn't exist in the second object\n")
|
||||
|
||||
self.compare_saif_instances(instance, second.child_instances[instance_name])
|
||||
|
||||
def compare_saif_contents(self, first: SAIFParser, second: SAIFParser):
|
||||
"""Test if second SAIF file has the same values as the first"""
|
||||
self.compare_saif_instances(first.top_instance, second.top_instance)
|
||||
|
||||
def print_saif_instance_tree(self, saif_instance: SAIFInstance):
|
||||
print(f"INSTANCE: {saif_instance.scope_name}")
|
||||
|
||||
print("NET:")
|
||||
for signal_name, signal in saif_instance.nets.items():
|
||||
for bit in range(signal.width):
|
||||
golden_bit = signal.bits[bit]
|
||||
|
||||
valid_signal_width = tested_object_signal.width > bit
|
||||
|
||||
if int(golden_bit.transitions) <= 0 and valid_signal_width and int(tested_object_signal.bits[bit].transitions) > 0:
|
||||
print(f"{signal.name}[{bit}] (T0 {golden_bit.low_time}) (T1 {golden_bit.high_time}) (TC {golden_bit.transitions})")
|
||||
print(f"{signal.name}[{bit}] (T0 {tested_object_signal.bits[bit].low_time}) (T1 {tested_object_signal.bits[bit].high_time}) (TC {tested_object_signal.bits[bit].transitions})")
|
||||
self.error("Transitions number mismatch!\n")
|
||||
|
||||
if valid_signal_width:
|
||||
tested_bit = tested_object_signal.bits[bit]
|
||||
if int(golden_bit.transitions) != int(tested_bit.transitions) or int(golden_bit.high_time) != int(tested_bit.high_time) or int(golden_bit.low_time) != int(tested_bit.low_time):
|
||||
print(f"{signal.name}[{bit}] (T0 {golden_bit.low_time}) (T1 {golden_bit.high_time}) (TC {golden_bit.transitions})")
|
||||
print(f"{signal.name}[{bit}] (T0 {tested_bit.low_time}) (T1 {tested_bit.high_time}) (TC {tested_bit.transitions})")
|
||||
self.error("Signal bit mismatch!\n")
|
||||
print(f"{signal.name}[{bit}] (T0 {signal.bits[bit].low_time}) (T1 {signal.bits[bit].high_time}) (TC {signal.bits[bit].transitions})")
|
||||
|
||||
for instance_name, instance in saif_instance.child_instances.items():
|
||||
self.print_saif_instance_tree(instance)
|
||||
|
||||
def saif_identical(self, fn1: str, fn2: str) -> None:
|
||||
"""Test if two SAIF files have logically-identical contents"""
|
||||
vcd_to_saif_parser = VCDToSAIFParser()
|
||||
vcd_to_saif_parser.parse(fn2)
|
||||
test_result_saif = SAIFParser()
|
||||
test_result_saif.parse(fn1)
|
||||
|
||||
saif_parser = SAIFParser()
|
||||
saif_parser.parse(fn1)
|
||||
golden_saif = SAIFParser()
|
||||
golden_saif.parse(fn2)
|
||||
|
||||
self.compare_saif_contents(vcd_to_saif_parser, saif_parser)
|
||||
self.compare_saif_contents(test_result_saif, golden_saif)
|
||||
self.compare_saif_contents(golden_saif, test_result_saif)
|
||||
|
||||
def _vcd_read(self, filename: str) -> str:
|
||||
data = {}
|
||||
|
|
|
|||
Loading…
Reference in New Issue