From d8076c05bbb254d4423c9c95cbbd8c898024a68f Mon Sep 17 00:00:00 2001 From: Fernando Garcia Redondo Date: Tue, 23 Jul 2024 15:56:48 +0100 Subject: [PATCH 1/2] added states --- vcd2json.py | 176 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 102 insertions(+), 74 deletions(-) diff --git a/vcd2json.py b/vcd2json.py index 96f1c85..d1acd72 100644 --- a/vcd2json.py +++ b/vcd2json.py @@ -1,4 +1,5 @@ """Create WaveJSON text string from VCD file.""" + import sys @@ -7,12 +8,12 @@ def __init__(self, name, sid, length): self._name = name self._sid = sid self._length = length - self._fmt = '' + self._fmt = "" class WaveExtractor: - def __init__(self, vcd_file, json_file, path_list): + def __init__(self, vcd_file, json_file, path_list, states_dict: dict = {}): """ Extract signal values from VCD file and output in JSON format. Specify VCD filename, JSON filename, and signal path list. @@ -23,10 +24,11 @@ def __init__(self, vcd_file, json_file, path_list): """ self._vcd_file = vcd_file self._json_file = json_file - self._path_list = [path.strip('/') for path in path_list] + self._path_list = [path.strip("/") for path in path_list] self._wave_chunk = 20 self._start_time = 0 self._end_time = 0 + self._states_dict = states_dict self._setup() @property @@ -69,17 +71,17 @@ def create_path_dict(fin): words = line.split() if not words: continue - if words[0] == '$enddefinitions': + if words[0] == "$enddefinitions": return path_list, path_dict - if words[0] == '$scope': + if words[0] == "$scope": hier_list.append(words[2]) - elif words[0] == '$var': - path = '/'.join(hier_list + [words[4]]) + elif words[0] == "$var": + path = "/".join(hier_list + [words[4]]) path_list.append(path) - path_dict[path] = _SignalDef(name=words[4], - sid=words[3], - length=int(words[2])) - elif words[0] == '$upscope': + path_dict[path] = _SignalDef( + name=words[4], sid=words[3], length=int(words[2]) + ) + elif words[0] == "$upscope": del hier_list[-1] def update_path_dict(path_list, path_dict): @@ -87,12 +89,17 @@ def update_path_dict(path_list, path_dict): for path in path_list: signal_def = path_dict.get(path, None) if not signal_def: - raise ValueError('Can\'t find path "{0}".'.format(path)) + raise ValueError(f'Can\'t find path "{path}".') new_path_dict[path] = signal_def return new_path_dict - fin = open(self._vcd_file, 'rt') + fin = open(self._vcd_file, "rt") path_list, path_dict = create_path_dict(fin) + + # INFO: + # list of available signals + for p in path_dict.items(): + print("DEBUG ", p[0], " sid: ", p[1]._sid) if self._path_list: path_dict = update_path_dict(self._path_list, path_dict) else: @@ -107,12 +114,12 @@ def print_props(self): """ print("vcd_file = '" + self._vcd_file + "'") print("json_file = '" + self._json_file + "'") - print("path_list = [", end='') + print("path_list = [", end="") for i, path in enumerate(self._path_list): if i != 0: - print(" ", end='') - print("'" + path + "'", end='') - if i != len(self._path_list)-1: + print(" ", end="") + print("'" + path + "'", end="") + if i != len(self._path_list) - 1: print(",") else: print("]") @@ -131,7 +138,7 @@ def wave_format(self, signal_path, fmt): 'x' - Hexa-decimal, lowercase is used. 'X' - Hexa-decimal, uppercase is used. """ - if fmt not in ('b', 'd', 'u', 'x', 'X'): + if fmt not in ("b", "d", "u", "x", "X"): raise ValueError('"{0}": Invalid format character.'.format(fmt)) self._path_dict[signal_path]._fmt = fmt return 0 @@ -150,23 +157,27 @@ def execute(self): clock_id = path_dict[path_list[0]]._sid id_list = [path_dict[path]._sid for path in path_list] - value_dict = {sid: 'x' for sid in id_list} + value_dict = {sid: "x" for sid in id_list} sample_dict = {sid: [] for sid in id_list} - if self._json_file == '': + states_dict = { + path_dict[k]._sid: d_val for k, d_val in self._states_dict.items() + } + + if self._json_file == "": fout = sys.stdout else: self.print_props() print() print('Create WaveJSON file "{0}".'.format(self._json_file)) - fout = open(self._json_file, 'wt') + fout = open(self._json_file, "wt") fout.write(jsongen.create_header()) while True: origin = sampler.run(fin, clock_id, value_dict, sample_dict) if len(sample_dict[clock_id]) == 0: break - fout.write(jsongen.create_body(origin, sample_dict)) + fout.write(jsongen.create_body(origin, sample_dict, states_dict)) fout.write(jsongen.create_footer()) fin.close() @@ -174,7 +185,7 @@ def execute(self): return 0 -class _SignalSampler(): +class _SignalSampler: def __init__(self, wave_chunk, start_time, end_time): self._wave_chunk = wave_chunk @@ -199,28 +210,31 @@ def run(self, fin, clock_id, value_dict, sample_dict): if not words: continue char = words[0][0] - if char == '$': + if char == "$": continue - if char == 'r': + if char == "r": continue - if char in ('0', '1', 'x', 'z'): + if char in ("0", "1", "x", "z"): sid = words[0][1:] if sid in value_dict: value_dict[sid] = char continue - if char == 'b': + if char == "b": sid = words[1] if sid in value_dict: value_dict[sid] = words[0][1:] continue - if char == '#': + if char == "#": next_now = words[0][1:] clock = value_dict[clock_id] - if clock_prev == '0' and clock == '1': + if clock_prev == "0" and clock == "1": if data_count == 0: origin = self._now - elif self._start_time <= int(origin) and \ - clock_prev == '1' and clock == '0': + elif ( + self._start_time <= int(origin) + and clock_prev == "1" + and clock == "0" + ): for sid in sample_dict: sample_dict[sid].append(value_dict[sid]) data_count += 1 @@ -233,91 +247,105 @@ def run(self, fin, clock_id, value_dict, sample_dict): raise ValueError('"{0}": Unexpected character.'.format(char)) -class _JsonGenerator(): +class _JsonGenerator: def __init__(self, path_list, path_dict, wave_chunk): self._path_list = path_list self._path_dict = path_dict self._wave_chunk = wave_chunk self._clock_name = path_dict[path_list[0]]._name - self._name_width = max([len(path_dict[path]._name) - for path in path_list]) + self._name_width = max([len(path_dict[path]._name) for path in path_list]) def create_header(self): name = '"{0}"'.format(self._clock_name).ljust(self._name_width + 2) - wave = '"{0}"'.format('p' + '.' * (self._wave_chunk - 1)) - json = '{ "head": {"tock":1},\n' + wave = '"{0}"'.format("p" + "." * (self._wave_chunk - 1)) + json = '{ "head": {"tock":1},\n' json += ' "signal": [\n' - json += ' { "name": '+name+', "wave": '+wave+' }' + json += ' { "name": ' + name + ', "wave": ' + wave + " }" return json - def create_body(self, origin, sample_dict): + def create_body(self, origin, sample_dict, states_dict: dict): def create_wave(samples): prev = None - wave = '' + wave = "" for value in samples: if value == prev: - wave += '.' + wave += "." else: wave += value prev = value - return '"'+wave+'"' + return '"' + wave + '"' - def create_wave_data(samples, length, fmt): + def create_wave_data(samples, length, fmt, sid, states_dict): prev = None - wave = '' - data = '' + wave = "" + data = "" for value in samples: if value == prev: - wave += '.' - elif all([c == '0' or c == '1' for c in value]): - wave += '=' - data += ' ' + data_format(value, length, fmt) - elif all([c == 'z' for c in value]): - wave += 'z' + wave += "." + elif all([c == "0" or c == "1" for c in value]): + wave += "=" + data += " " + data_format( + value, length, fmt, sid=sid, states_dict=states_dict + ) + elif all([c == "z" for c in value]): + wave += "z" else: - wave += 'x' + wave += "x" prev = value - return '"'+wave+'"', '"'+data[1:]+'"' + return '"' + wave + '"', '"' + data[1:] + '"' - def data_format(value, length, fmt): + def data_format(value, length, fmt, sid, states_dict): value = int(value, 2) - if fmt == 'b': - fmt = '0' + str(length) + 'b' - elif fmt == 'd': - if value >= 2**(length-1): + if fmt == "b": + fmt = "0" + str(length) + "b" + elif fmt == "d": + if value >= 2 ** (length - 1): value -= 2**length - elif fmt == 'u': - fmt = 'd' - elif fmt == 'X': - fmt = '0' + str((length+3)//4) + 'X' + elif fmt == "u": + fmt = "d" + elif fmt == "X": + fmt = "0" + str((length + 3) // 4) + "X" else: - fmt = '0' + str((length+3)//4) + 'x' + fmt = "0" + str((length + 3) // 4) + "x" + if sid in states_dict: + enc = states_dict[sid][value] + print(enc) + return enc return format(value, fmt) - json = ',\n' - json += ' {},\n' + json = ",\n" + json += " {},\n" json += ' ["{0}"'.format(origin) for path in self._path_list[1:]: - name = self._path_dict[path]._name - sid = self._path_dict[path]._sid + name = self._path_dict[path]._name + sid = self._path_dict[path]._sid length = self._path_dict[path]._length if length == 1: name = '"{0}"'.format(name).ljust(self._name_width + 2) wave = create_wave(sample_dict[sid]) - json += ',\n { "name": '+name+', "wave": '+wave+' }' + json += ',\n { "name": ' + name + ', "wave": ' + wave + " }" else: fmt = self._path_dict[path]._fmt name = '"{0}"'.format(name).ljust(self._name_width + 2) - wave, data = create_wave_data(sample_dict[sid], length, fmt) - json += ',\n { "name": '+name+', "wave": '+wave+\ - ', "data": '+data+' }' - json += '\n ]' + wave, data = create_wave_data( + sample_dict[sid], length, fmt, sid=sid, states_dict=states_dict + ) + json += ( + ',\n { "name": ' + + name + + ', "wave": ' + + wave + + ', "data": ' + + data + + " }" + ) + json += "\n ]" return json def create_footer(self): - json = '\n' - json += ' ]\n' - json += '}\n' + json = "\n" + json += " ]\n" + json += "}\n" return json From 5f4b42a8d426a4f9dc8cf74f301a46fe75e8b507 Mon Sep 17 00:00:00 2001 From: Fernando Garcia Redondo Date: Wed, 24 Jul 2024 09:01:54 +0100 Subject: [PATCH 2/2] Readme for states decoding --- README.md | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/README.md b/README.md index b61126f..7d3a3cb 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,68 @@ The following JSON formatted text is generated. ] } +## Optional: States: + +You can pass a dictionary with the FSM states of given signals and the tool will change the numerical format by the name of the state. +i.e. + + +```python +from vcd2json import WaveExtractor + +path_list = ['tb_timer/u_fsm/clock', + 'tb_timer/u_fsm/reset', + 'tb_timer/u_fsm/state'] +states_dict = { + "tb_timer/u_fsm/state[3:0]": { + 0: "IDLE", + 1: "STATE1", + 2: "STATE2", + 3: "STATE3", + 4: "STATE4", + 5: "STATE5", + 6: "STATE6", + 7: "STATE7", + 8: "STATE8", + 9: "STATE9", + }, +} + +extractor = WaveExtractor('PATH_TO_YOUR_VCD.vcd', 'fsm.json', path_list) +extractor.execute() +``` + +The following JSON formatted text is generated. + +``` + { "head": {"tock":1}, + "signal": [ + { "name": "clock" , "wave": "p..................." }, + {}, + ["10", + { "name": "reset" , "wave": "0...1....0.........." }, + { "name": "pulse" , "wave": "x.....0............." }, + { "name": "fsm" , "wave": "x....=....==========", "data": "IDLE STATE1 STATE2 STATE3 STATE4 STATE5 STATER6 STATE7 STATE 8 STATE9" } + ], + ] + } +``` + +Instead of +``` + { "head": {"tock":1}, + "signal": [ + { "name": "clock" , "wave": "p..................." }, + {}, + ["10", + { "name": "reset" , "wave": "0...1....0.........." }, + { "name": "pulse" , "wave": "x.....0............." }, + { "name": "state" , "wave": "x....=....==========", "data": "0 1 2 3 4 5 6 7 8 9" } + ], + ] + } +``` + You can create SVG image from WaveJSON by using JavaScript application WaveDrom. ![timing diagram](timer.png)