litescope: add support for groups of signals and dynamic muxing between them
allow multiple debug configuration in a single bitstream
This commit is contained in:
parent
2f625c58b2
commit
cf7ea12ec8
|
@ -66,6 +66,21 @@ class FrontendSubSampler(Module, AutoCSR):
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class AnalyzerMux(Module, AutoCSR):
|
||||||
|
def __init__(self, dw, n):
|
||||||
|
self.sinks = [stream.Endpoint(core_layout(dw)) for i in range(n)]
|
||||||
|
self.source = stream.Endpoint(core_layout(dw))
|
||||||
|
|
||||||
|
self.value = CSRStorage(bits_for(n))
|
||||||
|
|
||||||
|
# # #
|
||||||
|
|
||||||
|
cases = {}
|
||||||
|
for i in range(n):
|
||||||
|
cases[i] = self.sinks[i].connect(self.source)
|
||||||
|
self.comb += Case(self.value.storage, cases)
|
||||||
|
|
||||||
|
|
||||||
class AnalyzerFrontend(Module, AutoCSR):
|
class AnalyzerFrontend(Module, AutoCSR):
|
||||||
def __init__(self, dw, cd, cd_ratio):
|
def __init__(self, dw, cd, cd_ratio):
|
||||||
self.sink = stream.Endpoint(core_layout(dw))
|
self.sink = stream.Endpoint(core_layout(dw))
|
||||||
|
@ -160,8 +175,11 @@ class LiteScopeIO(Module, AutoCSR):
|
||||||
return self.gpio.get_csrs()
|
return self.gpio.get_csrs()
|
||||||
|
|
||||||
|
|
||||||
class LiteScopeAnalyzer(Module, AutoCSR):
|
def _format_groups(groups):
|
||||||
def __init__(self, signals, depth, cd="sys", cd_ratio=1):
|
if not isinstance(groups, dict):
|
||||||
|
groups = {0 : groups}
|
||||||
|
new_groups = {}
|
||||||
|
for n, signals in groups.items():
|
||||||
if not isinstance(signals, list):
|
if not isinstance(signals, list):
|
||||||
signals = [signals]
|
signals = [signals]
|
||||||
|
|
||||||
|
@ -171,32 +189,40 @@ class LiteScopeAnalyzer(Module, AutoCSR):
|
||||||
split_signals.extend(s.flatten())
|
split_signals.extend(s.flatten())
|
||||||
else:
|
else:
|
||||||
split_signals.append(s)
|
split_signals.append(s)
|
||||||
signals = split_signals
|
new_groups[n] = split_signals
|
||||||
|
return new_groups
|
||||||
|
|
||||||
self.signals = signals
|
|
||||||
self.dw = sum([len(s) for s in signals])
|
class LiteScopeAnalyzer(Module, AutoCSR):
|
||||||
self.core_dw = self.dw*cd_ratio
|
def __init__(self, groups, depth, cd="sys", cd_ratio=1):
|
||||||
|
self.groups = _format_groups(groups)
|
||||||
|
self.dw = max([sum([len(s) for s in g]) for g in self.groups.values()])
|
||||||
|
|
||||||
self.depth = depth
|
self.depth = depth
|
||||||
self.cd_ratio = cd_ratio
|
self.cd_ratio = cd_ratio
|
||||||
|
|
||||||
# # #
|
# # #
|
||||||
|
|
||||||
|
self.submodules.mux = AnalyzerMux(self.dw, len(self.groups))
|
||||||
|
for i, signals in self.groups.items():
|
||||||
|
self.comb += [
|
||||||
|
self.mux.sinks[i].valid.eq(1),
|
||||||
|
self.mux.sinks[i].data.eq(Cat(signals))
|
||||||
|
]
|
||||||
self.submodules.frontend = AnalyzerFrontend(self.dw, cd, cd_ratio)
|
self.submodules.frontend = AnalyzerFrontend(self.dw, cd, cd_ratio)
|
||||||
self.submodules.storage = AnalyzerStorage(self.core_dw, depth, cd_ratio)
|
self.submodules.storage = AnalyzerStorage(self.dw*cd_ratio, depth, cd_ratio)
|
||||||
|
|
||||||
self.comb += [
|
self.comb += [
|
||||||
self.frontend.sink.valid.eq(1),
|
self.mux.source.connect(self.frontend.sink),
|
||||||
self.frontend.sink.data.eq(Cat(self.signals)),
|
|
||||||
self.frontend.source.connect(self.storage.sink)
|
self.frontend.source.connect(self.storage.sink)
|
||||||
]
|
]
|
||||||
|
|
||||||
def export_csv(self, vns, filename):
|
def export_csv(self, vns, filename):
|
||||||
def format_line(*args):
|
def format_line(*args):
|
||||||
return ",".join(args) + "\n"
|
return ",".join(args) + "\n"
|
||||||
r = format_line("config", "dw", str(self.dw))
|
r = format_line("config", "None", "dw", str(self.dw))
|
||||||
r += format_line("config", "depth", str(self.depth))
|
r += format_line("config", "None", "depth", str(self.depth))
|
||||||
r += format_line("config", "cd_ratio", str(int(self.cd_ratio)))
|
r += format_line("config", "None", "cd_ratio", str(int(self.cd_ratio)))
|
||||||
for s in self.signals:
|
for i, signals in self.groups.items():
|
||||||
r += format_line("signal", vns.get_name(s), str(len(s)))
|
for s in signals:
|
||||||
|
r += format_line("signal", str(i), vns.get_name(s), str(len(s)))
|
||||||
write_to_file(filename, r)
|
write_to_file(filename, r)
|
||||||
|
|
|
@ -6,7 +6,8 @@ from litescope.software.dump import *
|
||||||
|
|
||||||
import csv
|
import csv
|
||||||
|
|
||||||
class LiteScopeAnalyzerDriver():
|
|
||||||
|
class LiteScopeAnalyzerDriver:
|
||||||
def __init__(self, regs, name, config_csv=None, clk_freq=None, debug=False):
|
def __init__(self, regs, name, config_csv=None, clk_freq=None, debug=False):
|
||||||
self.regs = regs
|
self.regs = regs
|
||||||
self.name = name
|
self.name = name
|
||||||
|
@ -21,24 +22,28 @@ class LiteScopeAnalyzerDriver():
|
||||||
self.samplerate = clk_freq
|
self.samplerate = clk_freq
|
||||||
self.debug = debug
|
self.debug = debug
|
||||||
self.get_config()
|
self.get_config()
|
||||||
self.get_layout()
|
self.get_layouts()
|
||||||
self.build()
|
self.build()
|
||||||
|
self.group = 0
|
||||||
self.data = DumpData(self.dw)
|
self.data = DumpData(self.dw)
|
||||||
|
|
||||||
def get_config(self):
|
def get_config(self):
|
||||||
csv_reader = csv.reader(open(self.config_csv), delimiter=',', quotechar='#')
|
csv_reader = csv.reader(open(self.config_csv), delimiter=',', quotechar='#')
|
||||||
for item in csv_reader:
|
for item in csv_reader:
|
||||||
t, n, v = item
|
t, g, n, v = item
|
||||||
if t == "config":
|
if t == "config":
|
||||||
setattr(self, n, int(v))
|
setattr(self, n, int(v))
|
||||||
|
|
||||||
def get_layout(self):
|
def get_layouts(self):
|
||||||
self.layout = []
|
self.layouts = {}
|
||||||
csv_reader = csv.reader(open(self.config_csv), delimiter=',', quotechar='#')
|
csv_reader = csv.reader(open(self.config_csv), delimiter=',', quotechar='#')
|
||||||
for item in csv_reader:
|
for item in csv_reader:
|
||||||
t, n, v = item
|
t, g, n, v = item
|
||||||
if t == "signal":
|
if t == "signal":
|
||||||
self.layout.append((n, int(v)))
|
try:
|
||||||
|
self.layouts[int(g)].append((n, int(v)))
|
||||||
|
except:
|
||||||
|
self.layouts[int(g)] = [(n, int(v))]
|
||||||
|
|
||||||
def build(self):
|
def build(self):
|
||||||
for key, value in self.regs.d.items():
|
for key, value in self.regs.d.items():
|
||||||
|
@ -46,13 +51,19 @@ class LiteScopeAnalyzerDriver():
|
||||||
key = key.replace(self.name + "_", "")
|
key = key.replace(self.name + "_", "")
|
||||||
setattr(self, key, value)
|
setattr(self, key, value)
|
||||||
value = 1
|
value = 1
|
||||||
for name, length in self.layout:
|
for signals in self.layouts.values():
|
||||||
setattr(self, name + "_o", value)
|
for name, length in signals:
|
||||||
value = value*(2**length)
|
setattr(self, name + "_o", value)
|
||||||
|
value = value*(2**length)
|
||||||
value = 0
|
value = 0
|
||||||
for name, length in self.layout:
|
for signals in self.layouts.values():
|
||||||
setattr(self, name + "_m", (2**length-1) << value)
|
for name, length in signals:
|
||||||
value += length
|
setattr(self, name + "_m", (2**length-1) << value)
|
||||||
|
value += length
|
||||||
|
|
||||||
|
def configure_group(self, value):
|
||||||
|
self.group = value
|
||||||
|
self.mux_value.write(value)
|
||||||
|
|
||||||
def configure_trigger(self, value=0, mask=0, cond=None):
|
def configure_trigger(self, value=0, mask=0, cond=None):
|
||||||
if cond is not None:
|
if cond is not None:
|
||||||
|
@ -113,5 +124,5 @@ class LiteScopeAnalyzerDriver():
|
||||||
dump = SigrokDump(samplerate=self.samplerate)
|
dump = SigrokDump(samplerate=self.samplerate)
|
||||||
else:
|
else:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
dump.add_from_layout(self.layout, self.data)
|
dump.add_from_layout(self.layouts[self.group], self.data)
|
||||||
dump.write(filename)
|
dump.write(filename)
|
||||||
|
|
Loading…
Reference in New Issue