VCD: Add samplerate support to fix displayed timestamps
To use this, pass the samplerate kwarg to LiteScopeAnalyzer(). If using the sys domain, soc_obj.sys_clk_freq works.
This commit is contained in:
parent
42a357714b
commit
21f6fcaa28
|
@ -48,6 +48,7 @@ class LiteScopeSoC(BaseSoC):
|
|||
self.submodules.analyzer = LiteScopeAnalyzer(analyzer_signals,
|
||||
depth = 1024,
|
||||
clock_domain = "sys",
|
||||
samplerate = self.sys_clk_freq,
|
||||
csr_csv = "analyzer.csv")
|
||||
self.add_csr("analyzer")
|
||||
|
||||
|
|
|
@ -226,9 +226,10 @@ class _Storage(Module, AutoCSR):
|
|||
|
||||
|
||||
class LiteScopeAnalyzer(Module, AutoCSR):
|
||||
def __init__(self, groups, depth, clock_domain="sys", trigger_depth=16, register=False, csr_csv="analyzer.csv"):
|
||||
def __init__(self, groups, depth, samplerate=1e-12, clock_domain="sys", trigger_depth=16, register=False, csr_csv="analyzer.csv"):
|
||||
self.groups = groups = self.format_groups(groups)
|
||||
self.depth = depth
|
||||
self.samplerate = samplerate
|
||||
|
||||
self.data_width = data_width = max([sum([len(s) for s in g]) for g in groups.values()])
|
||||
|
||||
|
@ -294,6 +295,7 @@ class LiteScopeAnalyzer(Module, AutoCSR):
|
|||
return ",".join(args) + "\n"
|
||||
r = format_line("config", "None", "data_width", str(self.data_width))
|
||||
r += format_line("config", "None", "depth", str(self.depth))
|
||||
r += format_line("config", "None", "samplerate", str(self.samplerate))
|
||||
for i, signals in self.groups.items():
|
||||
for s in signals:
|
||||
r += format_line("signal", str(i), vns.get_name(s), str(len(s)))
|
||||
|
|
|
@ -118,6 +118,7 @@ class LiteScopeAnalyzerDriver:
|
|||
self.add_trigger(value, mask, cond)
|
||||
|
||||
def configure_subsampler(self, value):
|
||||
self.subsampling = value
|
||||
self.subsampler_value.write(value-1)
|
||||
|
||||
def run(self, offset=0, length=None):
|
||||
|
@ -166,11 +167,13 @@ class LiteScopeAnalyzerDriver:
|
|||
return self.data
|
||||
|
||||
def save(self, filename, samplerate=None, flatten=False):
|
||||
if samplerate is None:
|
||||
samplerate = self.samplerate / self.subsampling
|
||||
if self.debug:
|
||||
print("[writing to " + filename + "]...")
|
||||
name, ext = os.path.splitext(filename)
|
||||
if ext == ".vcd":
|
||||
dump = VCDDump()
|
||||
dump = VCDDump(samplerate=samplerate)
|
||||
elif ext == ".csv":
|
||||
dump = CSVDump()
|
||||
elif ext == ".py":
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
from itertools import count
|
||||
import datetime
|
||||
import re
|
||||
from litescope.software.dump.common import Dump, dec2bin
|
||||
|
||||
|
||||
|
@ -19,14 +20,37 @@ def vcd_codes():
|
|||
code = codechars[r] + code
|
||||
yield code
|
||||
|
||||
_si_prefix2exp = {
|
||||
"": 0,
|
||||
"m": -3,
|
||||
"u": -6,
|
||||
"n": -9,
|
||||
"p": -12,
|
||||
"f": -15,
|
||||
}
|
||||
|
||||
def _timescale_str2num(timescale):
|
||||
match = re.fullmatch("(\d+)(\w{0,1})s", timescale)
|
||||
num = int(match.group(1))
|
||||
si_prefix = match.group(2)
|
||||
exp = _si_prefix2exp[si_prefix]
|
||||
return num * 10**exp, si_prefix
|
||||
|
||||
|
||||
class VCDDump(Dump):
|
||||
def __init__(self, dump=None, timescale="1ps", comment=""):
|
||||
def __init__(self, dump=None, samplerate=1e-12, timescale="1ps", comment=""):
|
||||
Dump.__init__(self)
|
||||
self.variables = [] if dump is None else dump.variables
|
||||
self.timescale = timescale
|
||||
self.comment = comment
|
||||
self.cnt = -1
|
||||
# rescale the timescale from the provided one to one where it is equal to the samplerate
|
||||
# this lets us output sequential change timestamps which helps with software like PulseView
|
||||
# that slow down if a much smaller timescale than necessary is used
|
||||
timescale_seconds, si_prefix = _timescale_str2num(timescale)
|
||||
# factor of 2 scale is because of 2x samples from fake clock
|
||||
self.count_timescale = int(1 / (timescale_seconds * samplerate * 2))
|
||||
self.timescale_unit_str = si_prefix + "s"
|
||||
|
||||
def change(self):
|
||||
r = ""
|
||||
|
@ -65,14 +89,12 @@ class VCDDump(Dump):
|
|||
|
||||
def generate_timescale(self):
|
||||
r = "$timescale "
|
||||
r += self.timescale
|
||||
r += str(self.count_timescale) + self.timescale_unit_str
|
||||
r += " $end\n"
|
||||
return r
|
||||
|
||||
def generate_vars(self):
|
||||
r = "$scope "
|
||||
r += self.timescale
|
||||
r += " $end\n"
|
||||
r = "$scope dumped_signals $end\n"
|
||||
for v in self.variables:
|
||||
r += "$var wire "
|
||||
r += str(v.width)
|
||||
|
|
Loading…
Reference in New Issue