[#72179] move SAIF parsing code to driver.py and add it to SAIF testing pipeline

This commit is contained in:
Mateusz Gancarz 2025-02-18 08:21:23 +01:00
parent 94de419932
commit 0e2cf72966
3 changed files with 791 additions and 149 deletions

View File

@ -66,6 +66,142 @@ class staticproperty(property):
def __get__(self, owner_self, owner_cls):
return self.fget()
#######################################################################
#######################################################################
# SAIF parser utilities
class SAIFSignalBit:
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):
self.transitions += 1
if self.last_val:
self.high_time += dt
self.last_val = new_val
class SAIFSignal:
def __init__(self, signal_name, signal_width = 0):
self.name = signal_name
self.width = signal_width
self.last_time = 0
self.bits = []
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 SAIFParser:
def __init__(self):
self.signal_definitions = {}
def parse(self, saif_filename):
with open(saif_filename, 'r') as saif_file:
for line in saif_file:
line = line.strip()
if not line:
continue
match = re.search(r'(\w+)\[*(\d*)\]*\s+(\(T(.+)\))+', line)
if match:
signal_name, bit_index, bit_values, _ = match.groups()
if bit_index == '':
bit_index = 0
if signal_name not in self.signal_definitions:
self.signal_definitions[signal_name] = SAIFSignal(signal_name)
for _ in range(0, int(bit_index) - self.signal_definitions[signal_name].width + 1):
self.signal_definitions[signal_name].bits.append(SAIFSignalBit())
self.signal_definitions[signal_name].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
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
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
#######################################################################
#######################################################################
@ -2368,9 +2504,38 @@ 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]
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")
def saif_identical(self, fn1: str, fn2: str) -> None:
"""Test if two SAIF files have logically-identical contents"""
#TODO: implement checking if two SAIF file are logically-identical
vcd_to_saif_parser = VCDToSAIFParser()
vcd_to_saif_parser.parse(fn2)
saif_parser = SAIFParser()
saif_parser.parse(fn1)
self.compare_saif_contents(vcd_to_saif_parser, saif_parser)
def _vcd_read(self, filename: str) -> str:
data = {}

View File

@ -0,0 +1,625 @@
$version Generated by VerilatedVcd $end
$timescale 1ps $end
$scope module top $end
$var wire 1 # clk $end
$var wire 8 $ state [7:0] $end
$scope module t $end
$var wire 1 # clk $end
$var wire 1 % rst $end
$var wire 8 $ state [7:0] $end
$var wire 32 & cyc [31:0] $end
$scope module c0 $end
$var wire 1 # clk $end
$var wire 1 % rst $end
$var wire 8 $ out [7:0] $end
$upscope $end
$upscope $end
$upscope $end
$enddefinitions $end
#0
0#
b00000000 $
0%
b00000000000000000000000000000000 &
#10
1#
1%
b00000000000000000000000000000001 &
#15
0#
#20
1#
b00000001 $
b00000000000000000000000000000010 &
#25
0#
#30
1#
b00000010 $
b00000000000000000000000000000011 &
#35
0#
#40
1#
b00000011 $
b00000000000000000000000000000100 &
#45
0#
#50
1#
b00000100 $
b00000000000000000000000000000101 &
#55
0#
#60
1#
b00000101 $
b00000000000000000000000000000110 &
#65
0#
#70
1#
b00000110 $
b00000000000000000000000000000111 &
#75
0#
#80
1#
b00000111 $
b00000000000000000000000000001000 &
#85
0#
#90
1#
b00001000 $
b00000000000000000000000000001001 &
#95
0#
#100
1#
b00001001 $
b00000000000000000000000000001010 &
#105
0#
#110
1#
b00001010 $
0%
b00000000000000000000000000001011 &
#115
0#
#120
1#
b00000000 $
1%
b00000000000000000000000000001100 &
#125
0#
#130
1#
b00000001 $
b00000000000000000000000000001101 &
#135
0#
#140
1#
b00000010 $
b00000000000000000000000000001110 &
#145
0#
#150
1#
b00000011 $
b00000000000000000000000000001111 &
#155
0#
#160
1#
b00000100 $
b00000000000000000000000000010000 &
#165
0#
#170
1#
b00000101 $
b00000000000000000000000000010001 &
#175
0#
#180
1#
b00000110 $
b00000000000000000000000000010010 &
#185
0#
#190
1#
b00000111 $
b00000000000000000000000000010011 &
#195
0#
#200
1#
b00001000 $
b00000000000000000000000000010100 &
#205
0#
#210
1#
b00001001 $
b00000000000000000000000000010101 &
#215
0#
#220
1#
b00001010 $
b00000000000000000000000000010110 &
#225
0#
#230
1#
b00001011 $
b00000000000000000000000000010111 &
#235
0#
#240
1#
b00001100 $
b00000000000000000000000000011000 &
#245
0#
#250
1#
b00001101 $
b00000000000000000000000000011001 &
#255
0#
#260
1#
b00001110 $
b00000000000000000000000000011010 &
#265
0#
#270
1#
b00001111 $
b00000000000000000000000000011011 &
#275
0#
#280
1#
b00010000 $
b00000000000000000000000000011100 &
#285
0#
#290
1#
b00010001 $
b00000000000000000000000000011101 &
#295
0#
#300
1#
b00010010 $
b00000000000000000000000000011110 &
#305
0#
#310
1#
b00010011 $
b00000000000000000000000000011111 &
#315
0#
#320
1#
b00010100 $
b00000000000000000000000000100000 &
#325
0#
#330
1#
b00010101 $
b00000000000000000000000000100001 &
#335
0#
#340
1#
b00010110 $
b00000000000000000000000000100010 &
#345
0#
#350
1#
b00010111 $
b00000000000000000000000000100011 &
#355
0#
#360
1#
b00011000 $
b00000000000000000000000000100100 &
#365
0#
#370
1#
b00011001 $
b00000000000000000000000000100101 &
#375
0#
#380
1#
b00011010 $
b00000000000000000000000000100110 &
#385
0#
#390
1#
b00011011 $
b00000000000000000000000000100111 &
#395
0#
#400
1#
b00011100 $
b00000000000000000000000000101000 &
#405
0#
#410
1#
b00011101 $
b00000000000000000000000000101001 &
#415
0#
#420
1#
b00011110 $
b00000000000000000000000000101010 &
#425
0#
#430
1#
b00011111 $
b00000000000000000000000000101011 &
#435
0#
#440
1#
b00100000 $
b00000000000000000000000000101100 &
#445
0#
#450
1#
b00100001 $
b00000000000000000000000000101101 &
#455
0#
#460
1#
b00100010 $
b00000000000000000000000000101110 &
#465
0#
#470
1#
b00100011 $
b00000000000000000000000000101111 &
#475
0#
#480
1#
b00100100 $
b00000000000000000000000000110000 &
#485
0#
#490
1#
b00100101 $
b00000000000000000000000000110001 &
#495
0#
#500
1#
b00100110 $
b00000000000000000000000000110010 &
#505
0#
#510
1#
b00100111 $
b00000000000000000000000000110011 &
#515
0#
#520
1#
b00101000 $
b00000000000000000000000000110100 &
#525
0#
#530
1#
b00101001 $
b00000000000000000000000000110101 &
#535
0#
#540
1#
b00101010 $
b00000000000000000000000000110110 &
#545
0#
#550
1#
b00101011 $
b00000000000000000000000000110111 &
#555
0#
#560
1#
b00101100 $
b00000000000000000000000000111000 &
#565
0#
#570
1#
b00101101 $
b00000000000000000000000000111001 &
#575
0#
#580
1#
b00101110 $
b00000000000000000000000000111010 &
#585
0#
#590
1#
b00101111 $
b00000000000000000000000000111011 &
#595
0#
#600
1#
b00110000 $
b00000000000000000000000000111100 &
#605
0#
#610
1#
b00110001 $
b00000000000000000000000000111101 &
#615
0#
#620
1#
b00110010 $
b00000000000000000000000000111110 &
#625
0#
#630
1#
b00110011 $
b00000000000000000000000000111111 &
#635
0#
#640
1#
b00110100 $
b00000000000000000000000001000000 &
#645
0#
#650
1#
b00110101 $
b00000000000000000000000001000001 &
#655
0#
#660
1#
b00110110 $
b00000000000000000000000001000010 &
#665
0#
#670
1#
b00110111 $
b00000000000000000000000001000011 &
#675
0#
#680
1#
b00111000 $
b00000000000000000000000001000100 &
#685
0#
#690
1#
b00111001 $
b00000000000000000000000001000101 &
#695
0#
#700
1#
b00111010 $
b00000000000000000000000001000110 &
#705
0#
#710
1#
b00111011 $
b00000000000000000000000001000111 &
#715
0#
#720
1#
b00111100 $
b00000000000000000000000001001000 &
#725
0#
#730
1#
b00111101 $
b00000000000000000000000001001001 &
#735
0#
#740
1#
b00111110 $
b00000000000000000000000001001010 &
#745
0#
#750
1#
b00111111 $
b00000000000000000000000001001011 &
#755
0#
#760
1#
b01000000 $
b00000000000000000000000001001100 &
#765
0#
#770
1#
b01000001 $
b00000000000000000000000001001101 &
#775
0#
#780
1#
b01000010 $
b00000000000000000000000001001110 &
#785
0#
#790
1#
b01000011 $
b00000000000000000000000001001111 &
#795
0#
#800
1#
b01000100 $
b00000000000000000000000001010000 &
#805
0#
#810
1#
b01000101 $
b00000000000000000000000001010001 &
#815
0#
#820
1#
b01000110 $
b00000000000000000000000001010010 &
#825
0#
#830
1#
b01000111 $
b00000000000000000000000001010011 &
#835
0#
#840
1#
b01001000 $
b00000000000000000000000001010100 &
#845
0#
#850
1#
b01001001 $
b00000000000000000000000001010101 &
#855
0#
#860
1#
b01001010 $
b00000000000000000000000001010110 &
#865
0#
#870
1#
b01001011 $
b00000000000000000000000001010111 &
#875
0#
#880
1#
b01001100 $
b00000000000000000000000001011000 &
#885
0#
#890
1#
b01001101 $
b00000000000000000000000001011001 &
#895
0#
#900
1#
b01001110 $
b00000000000000000000000001011010 &
#905
0#
#910
1#
b01001111 $
b00000000000000000000000001011011 &
#915
0#
#920
1#
b01010000 $
b00000000000000000000000001011100 &
#925
0#
#930
1#
b01010001 $
b00000000000000000000000001011101 &
#935
0#
#940
1#
b01010010 $
b00000000000000000000000001011110 &
#945
0#
#950
1#
b01010011 $
b00000000000000000000000001011111 &
#955
0#
#960
1#
b01010100 $
b00000000000000000000000001100000 &
#965
0#
#970
1#
b01010101 $
b00000000000000000000000001100001 &
#975
0#
#980
1#
b01010110 $
b00000000000000000000000001100010 &
#985
0#
#990
1#
b01010111 $
b00000000000000000000000001100011 &
#995
0#
#1000
1#
b01011000 $
b00000000000000000000000001100100 &

View File

@ -1,148 +0,0 @@
import re
from collections import defaultdict
class SAIFSignalBit:
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):
self.transitions += 1
if self.last_val:
self.high_time += dt
self.last_val = new_val
class SAIFSignal:
def __init__(self, signal_name, signal_width = 0):
self.name = signal_name
self.width = signal_width
self.last_time = 0
self.bits = []
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()
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 SAIFParser:
def __init__(self):
self.signal_definitions = {}
def parse(self, saif_filename):
with open(saif_filename, 'r') as saif_file:
for line in saif_file:
line = line.strip()
if not line:
continue
match = re.search(r'(\w+)\[*(\d*)\]*\s+(\(T(.+)\))+', line)
if match:
signal_name, bit_index, bit_values, _ = match.groups()
if bit_index == '':
bit_index = 0
if signal_name not in self.signal_definitions:
self.signal_definitions[signal_name] = SAIFSignal(signal_name)
for _ in range(0, int(bit_index) - self.signal_definitions[signal_name].width + 1):
self.signal_definitions[signal_name].bits.append(SAIFSignalBit())
self.signal_definitions[signal_name].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
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
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
vcd_to_saif_parser = VCDToSAIFParser()
vcd_to_saif_parser.parse("simx.vcd")
saif_parser = SAIFParser()
saif_parser.parse("simx.saif")
print("From VCD")
for signal_name, signal in vcd_to_saif_parser.signal_definitions.items():
for bit in range(signal.width):
print(f"{signal.name}[{bit}] (T0 {signal.bits[bit].low_time}) (T1 {signal.bits[bit].high_time}) (TC {signal.bits[bit].transitions})")
print()
print("From SAIF")
for signal_name, signal in saif_parser.signal_definitions.items():
for bit in range(signal.width):
print(f"{signal.name}[{bit}] (T0 {signal.bits[bit].low_time}) (T1 {signal.bits[bit].high_time}) (TC {signal.bits[bit].transitions})")