core: simplify and run storage in "scope" clock domain to get rid of cd_ratio.
This commit is contained in:
parent
9d5e605df3
commit
8d4c1ddcf9
|
@ -1,5 +1,6 @@
|
||||||
from migen import *
|
from migen import *
|
||||||
from migen.genlib.cdc import MultiReg
|
from migen.genlib.misc import WaitTimer
|
||||||
|
from migen.genlib.cdc import MultiReg, PulseSynchronizer
|
||||||
|
|
||||||
from litex.build.tools import write_to_file
|
from litex.build.tools import write_to_file
|
||||||
|
|
||||||
|
@ -22,14 +23,14 @@ class LiteScopeIO(Module, AutoCSR):
|
||||||
return self.gpio.get_csrs()
|
return self.gpio.get_csrs()
|
||||||
|
|
||||||
|
|
||||||
def core_layout(dw, hw=1):
|
def core_layout(dw):
|
||||||
return [("data", dw), ("hit", hw)]
|
return [("data", dw), ("hit", 1)]
|
||||||
|
|
||||||
|
|
||||||
class FrontendTrigger(Module, AutoCSR):
|
class FrontendTrigger(Module, AutoCSR):
|
||||||
def __init__(self, dw):
|
def __init__(self, dw):
|
||||||
self.sink = stream.Endpoint(core_layout(dw))
|
self.sink = sink = stream.Endpoint(core_layout(dw))
|
||||||
self.source = stream.Endpoint(core_layout(dw))
|
self.source = source = stream.Endpoint(core_layout(dw))
|
||||||
|
|
||||||
self.value = CSRStorage(dw)
|
self.value = CSRStorage(dw)
|
||||||
self.mask = CSRStorage(dw)
|
self.mask = CSRStorage(dw)
|
||||||
|
@ -39,90 +40,84 @@ class FrontendTrigger(Module, AutoCSR):
|
||||||
value = Signal(dw)
|
value = Signal(dw)
|
||||||
mask = Signal(dw)
|
mask = Signal(dw)
|
||||||
self.specials += [
|
self.specials += [
|
||||||
MultiReg(self.value.storage, value),
|
MultiReg(self.value.storage, value, "scope"),
|
||||||
MultiReg(self.mask.storage, mask)
|
MultiReg(self.mask.storage, mask, "scope")
|
||||||
]
|
]
|
||||||
|
|
||||||
self.comb += [
|
self.comb += [
|
||||||
self.sink.connect(self.source),
|
sink.connect(source),
|
||||||
self.source.hit.eq((self.sink.data & mask) == value)
|
source.hit.eq((sink.data & mask) == value)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class FrontendSubSampler(Module, AutoCSR):
|
class FrontendSubSampler(Module, AutoCSR):
|
||||||
def __init__(self, dw):
|
def __init__(self, dw):
|
||||||
self.sink = stream.Endpoint(core_layout(dw))
|
self.sink = sink = stream.Endpoint(core_layout(dw))
|
||||||
self.source = stream.Endpoint(core_layout(dw))
|
self.source = source = stream.Endpoint(core_layout(dw))
|
||||||
|
|
||||||
self.value = CSRStorage(16)
|
self.value = CSRStorage(16)
|
||||||
|
|
||||||
# # #
|
# # #
|
||||||
|
|
||||||
value = Signal(16)
|
value = Signal(16)
|
||||||
self.specials += MultiReg(self.value.storage, value)
|
self.specials += MultiReg(self.value.storage, value, "scope")
|
||||||
|
|
||||||
counter = Signal(16)
|
counter = Signal(16)
|
||||||
done = Signal()
|
done = Signal()
|
||||||
|
|
||||||
self.sync += \
|
self.sync.scope += \
|
||||||
If(self.source.ready,
|
If(source.ready,
|
||||||
If(done,
|
If(done,
|
||||||
counter.eq(0)
|
counter.eq(0)
|
||||||
).Elif(self.sink.valid,
|
).Elif(sink.valid,
|
||||||
counter.eq(counter + 1)
|
counter.eq(counter + 1)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
self.comb += [
|
self.comb += [
|
||||||
done.eq(counter == value),
|
done.eq(counter == value),
|
||||||
self.sink.connect(self.source, omit=set(["valid"])),
|
sink.connect(source, omit={"valid"}),
|
||||||
self.source.valid.eq(self.sink.valid & done)
|
source.valid.eq(sink.valid & done)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class AnalyzerMux(Module, AutoCSR):
|
class AnalyzerMux(Module, AutoCSR):
|
||||||
def __init__(self, dw, n):
|
def __init__(self, dw, n):
|
||||||
self.sinks = [stream.Endpoint(core_layout(dw)) for i in range(n)]
|
self.sinks = sinks = [stream.Endpoint(core_layout(dw)) for i in range(n)]
|
||||||
self.source = stream.Endpoint(core_layout(dw))
|
self.source = source = stream.Endpoint(core_layout(dw))
|
||||||
|
|
||||||
self.value = CSRStorage(bits_for(n))
|
self.value = CSRStorage(bits_for(n))
|
||||||
|
|
||||||
# # #
|
# # #
|
||||||
|
|
||||||
|
value = Signal(max=n)
|
||||||
|
self.specials += MultiReg(self.value.storage, value, "scope")
|
||||||
|
|
||||||
cases = {}
|
cases = {}
|
||||||
for i in range(n):
|
for i in range(n):
|
||||||
cases[i] = self.sinks[i].connect(self.source)
|
cases[i] = sinks[i].connect(source)
|
||||||
self.comb += Case(self.value.storage, cases)
|
self.comb += Case(value, cases)
|
||||||
|
|
||||||
|
|
||||||
class AnalyzerFrontend(Module, AutoCSR):
|
class AnalyzerFrontend(Module, AutoCSR):
|
||||||
def __init__(self, dw, cd_ratio):
|
def __init__(self, dw):
|
||||||
self.sink = stream.Endpoint(core_layout(dw))
|
self.sink = stream.Endpoint(core_layout(dw))
|
||||||
self.source = stream.Endpoint(core_layout(dw*cd_ratio))
|
self.source = stream.Endpoint(core_layout(dw))
|
||||||
|
|
||||||
# # #
|
# # #
|
||||||
|
|
||||||
self.submodules.buffer = stream.Buffer(core_layout(dw))
|
|
||||||
self.submodules.trigger = FrontendTrigger(dw)
|
self.submodules.trigger = FrontendTrigger(dw)
|
||||||
self.submodules.subsampler = FrontendSubSampler(dw)
|
self.submodules.subsampler = FrontendSubSampler(dw)
|
||||||
self.submodules.converter = stream.StrideConverter(
|
|
||||||
core_layout(dw, 1), core_layout(dw*cd_ratio, cd_ratio))
|
|
||||||
self.submodules.fifo = ClockDomainsRenamer(
|
|
||||||
{"write": "sys", "read": "new_sys"})(
|
|
||||||
stream.AsyncFIFO(core_layout(dw*cd_ratio, cd_ratio), 8))
|
|
||||||
self.submodules.pipeline = stream.Pipeline(
|
self.submodules.pipeline = stream.Pipeline(
|
||||||
self.sink,
|
self.sink,
|
||||||
self.buffer,
|
|
||||||
self.trigger,
|
self.trigger,
|
||||||
self.subsampler,
|
self.subsampler,
|
||||||
self.converter,
|
|
||||||
self.fifo,
|
|
||||||
self.source)
|
self.source)
|
||||||
|
|
||||||
|
|
||||||
class AnalyzerStorage(Module, AutoCSR):
|
class AnalyzerStorage(Module, AutoCSR):
|
||||||
def __init__(self, dw, depth, cd_ratio):
|
def __init__(self, dw, depth):
|
||||||
self.sink = stream.Endpoint(core_layout(dw, cd_ratio))
|
self.sink = sink = stream.Endpoint(core_layout(dw))
|
||||||
|
|
||||||
self.start = CSR()
|
self.start = CSR()
|
||||||
self.length = CSRStorage(bits_for(depth))
|
self.length = CSRStorage(bits_for(depth))
|
||||||
|
@ -132,47 +127,90 @@ class AnalyzerStorage(Module, AutoCSR):
|
||||||
self.wait = CSRStatus()
|
self.wait = CSRStatus()
|
||||||
self.run = CSRStatus()
|
self.run = CSRStatus()
|
||||||
|
|
||||||
self.mem_flush = CSR()
|
|
||||||
self.mem_valid = CSRStatus()
|
self.mem_valid = CSRStatus()
|
||||||
self.mem_ready = CSR()
|
self.mem_ready = CSR()
|
||||||
self.mem_data = CSRStatus(dw)
|
self.mem_data = CSRStatus(dw)
|
||||||
|
|
||||||
# # #
|
# # #
|
||||||
|
|
||||||
mem = stream.SyncFIFO([("data", dw)], depth//cd_ratio, buffered=True)
|
|
||||||
self.submodules += ResetInserter()(mem)
|
|
||||||
self.comb += mem.reset.eq(self.mem_flush.re)
|
|
||||||
|
|
||||||
|
# control re-synchronization
|
||||||
|
start = Signal()
|
||||||
|
length = Signal(max=depth)
|
||||||
|
offset = Signal(max=depth)
|
||||||
|
|
||||||
|
start_ps = PulseSynchronizer("sys", "scope")
|
||||||
|
self.submodules += start_ps
|
||||||
|
self.comb += start_ps.i.eq(self.start.re)
|
||||||
|
self.specials += [
|
||||||
|
MultiReg(self.length.storage, length, "scope"),
|
||||||
|
MultiReg(self.offset.storage, offset, "scope")
|
||||||
|
]
|
||||||
|
|
||||||
|
# status re-synchronization
|
||||||
|
idle = Signal()
|
||||||
|
wait = Signal()
|
||||||
|
run = Signal()
|
||||||
|
self.specials += [
|
||||||
|
MultiReg(idle, self.idle.status),
|
||||||
|
MultiReg(wait, self.wait.status),
|
||||||
|
MultiReg(run, self.run.status)
|
||||||
|
]
|
||||||
|
|
||||||
|
# memory
|
||||||
|
mem = stream.SyncFIFO([("data", dw)], depth, buffered=True)
|
||||||
|
mem = ClockDomainsRenamer("scope")(mem)
|
||||||
|
cdc = stream.AsyncFIFO([("data", dw)], 4)
|
||||||
|
cdc = ClockDomainsRenamer(
|
||||||
|
{"write": "scope", "read": "sys"})(cdc)
|
||||||
|
self.submodules += mem, cdc
|
||||||
|
|
||||||
|
# flush
|
||||||
|
mem_flush = WaitTimer(depth)
|
||||||
|
mem_flush = ClockDomainsRenamer("scope")(mem_flush)
|
||||||
|
self.submodules += mem_flush
|
||||||
|
|
||||||
|
# fsm
|
||||||
fsm = FSM(reset_state="IDLE")
|
fsm = FSM(reset_state="IDLE")
|
||||||
|
fsm = ClockDomainsRenamer("scope")(fsm)
|
||||||
self.submodules += fsm
|
self.submodules += fsm
|
||||||
|
|
||||||
fsm.act("IDLE",
|
fsm.act("IDLE",
|
||||||
self.idle.status.eq(1),
|
idle.eq(1),
|
||||||
If(self.start.re,
|
If(start_ps.o,
|
||||||
NextState("WAIT")
|
NextState("FLUSH")
|
||||||
),
|
),
|
||||||
self.sink.ready.eq(1),
|
sink.ready.eq(1),
|
||||||
mem.source.ready.eq(self.mem_ready.re & self.mem_ready.r)
|
mem.source.connect(cdc.sink)
|
||||||
|
)
|
||||||
|
fsm.act("FLUSH",
|
||||||
|
sink.ready.eq(1),
|
||||||
|
mem_flush.wait.eq(1),
|
||||||
|
mem.source.ready.eq(1),
|
||||||
|
If(mem_flush.done,
|
||||||
|
NextState("WAIT")
|
||||||
|
)
|
||||||
)
|
)
|
||||||
fsm.act("WAIT",
|
fsm.act("WAIT",
|
||||||
self.wait.status.eq(1),
|
wait.eq(1),
|
||||||
self.sink.connect(mem.sink, omit=set(["hit"])),
|
sink.connect(mem.sink, omit={"hit"}),
|
||||||
If(self.sink.valid & (self.sink.hit != 0),
|
If(sink.valid & sink.hit,
|
||||||
NextState("RUN")
|
NextState("RUN")
|
||||||
),
|
),
|
||||||
mem.source.ready.eq(mem.level >= self.offset.storage)
|
mem.source.ready.eq(mem.level >= self.offset.storage)
|
||||||
)
|
)
|
||||||
fsm.act("RUN",
|
fsm.act("RUN",
|
||||||
self.run.status.eq(1),
|
run.eq(1),
|
||||||
self.sink.connect(mem.sink, omit=set(["hit"])),
|
sink.connect(mem.sink, omit={"hit"}),
|
||||||
If(~mem.sink.ready | (mem.level >= self.length.storage),
|
If(mem.level >= self.length.storage,
|
||||||
NextState("IDLE"),
|
NextState("IDLE"),
|
||||||
mem.source.ready.eq(1)
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# memory read
|
||||||
self.comb += [
|
self.comb += [
|
||||||
self.mem_valid.status.eq(mem.source.valid),
|
self.mem_valid.status.eq(cdc.source.valid),
|
||||||
self.mem_data.status.eq(mem.source.data)
|
cdc.source.ready.eq(self.mem_ready.re),
|
||||||
|
self.mem_data.status.eq(cdc.source.data)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -195,24 +233,28 @@ def _format_groups(groups):
|
||||||
|
|
||||||
|
|
||||||
class LiteScopeAnalyzer(Module, AutoCSR):
|
class LiteScopeAnalyzer(Module, AutoCSR):
|
||||||
def __init__(self, groups, depth, cd="sys", cd_ratio=1):
|
def __init__(self, groups, depth, cd="sys"):
|
||||||
self.groups = _format_groups(groups)
|
self.groups = _format_groups(groups)
|
||||||
self.dw = max([sum([len(s) for s in g]) for g in self.groups.values()])
|
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.clock_domains.cd_scope = ClockDomain()
|
||||||
|
self.comb += [
|
||||||
|
self.cd_scope.clk.eq(ClockSignal(cd)),
|
||||||
|
self.cd_scope.rst.eq(ResetSignal(cd))
|
||||||
|
]
|
||||||
|
|
||||||
self.submodules.mux = AnalyzerMux(self.dw, len(self.groups))
|
self.submodules.mux = AnalyzerMux(self.dw, len(self.groups))
|
||||||
for i, signals in self.groups.items():
|
for i, signals in self.groups.items():
|
||||||
self.comb += [
|
self.comb += [
|
||||||
self.mux.sinks[i].valid.eq(1),
|
self.mux.sinks[i].valid.eq(1),
|
||||||
self.mux.sinks[i].data.eq(Cat(signals))
|
self.mux.sinks[i].data.eq(Cat(signals))
|
||||||
]
|
]
|
||||||
self.submodules.frontend = ClockDomainsRenamer(
|
self.submodules.frontend = AnalyzerFrontend(self.dw)
|
||||||
{"sys": cd, "new_sys": "sys"})(AnalyzerFrontend(self.dw, cd_ratio))
|
self.submodules.storage = AnalyzerStorage(self.dw, depth)
|
||||||
self.submodules.storage = AnalyzerStorage(self.dw*cd_ratio, depth, cd_ratio)
|
|
||||||
self.comb += [
|
self.comb += [
|
||||||
self.mux.source.connect(self.frontend.sink),
|
self.mux.source.connect(self.frontend.sink),
|
||||||
self.frontend.source.connect(self.storage.sink)
|
self.frontend.source.connect(self.storage.sink)
|
||||||
|
@ -223,7 +265,6 @@ class LiteScopeAnalyzer(Module, AutoCSR):
|
||||||
return ",".join(args) + "\n"
|
return ",".join(args) + "\n"
|
||||||
r = format_line("config", "None", "dw", str(self.dw))
|
r = format_line("config", "None", "dw", str(self.dw))
|
||||||
r += format_line("config", "None", "depth", str(self.depth))
|
r += format_line("config", "None", "depth", str(self.depth))
|
||||||
r += format_line("config", "None", "cd_ratio", str(int(self.cd_ratio)))
|
|
||||||
for i, signals in self.groups.items():
|
for i, signals in self.groups.items():
|
||||||
for s in signals:
|
for s in signals:
|
||||||
r += format_line("signal", str(i), vns.get_name(s), str(len(s)))
|
r += format_line("signal", str(i), vns.get_name(s), str(len(s)))
|
||||||
|
|
|
@ -75,7 +75,9 @@ class LiteScopeAnalyzerDriver:
|
||||||
self.frontend_subsampler_value.write(value-1)
|
self.frontend_subsampler_value.write(value-1)
|
||||||
|
|
||||||
def run(self, offset, length):
|
def run(self, offset, length):
|
||||||
self.storage_mem_flush.write(1)
|
# flush cdc
|
||||||
|
for i in range(4):
|
||||||
|
self.storage_mem_ready.write(1)
|
||||||
if self.debug:
|
if self.debug:
|
||||||
print("[running]...")
|
print("[running]...")
|
||||||
self.storage_offset.write(offset)
|
self.storage_offset.write(offset)
|
||||||
|
@ -92,7 +94,7 @@ class LiteScopeAnalyzerDriver:
|
||||||
def upload(self):
|
def upload(self):
|
||||||
if self.debug:
|
if self.debug:
|
||||||
print("[uploading]...")
|
print("[uploading]...")
|
||||||
length = self.storage_length.read()//self.cd_ratio
|
length = self.storage_length.read()
|
||||||
for position in range(1, length + 1):
|
for position in range(1, length + 1):
|
||||||
if self.debug:
|
if self.debug:
|
||||||
sys.stdout.write("|{}>{}| {}%\r".format('=' * (20*position//length),
|
sys.stdout.write("|{}>{}| {}%\r".format('=' * (20*position//length),
|
||||||
|
@ -103,12 +105,6 @@ class LiteScopeAnalyzerDriver:
|
||||||
self.storage_mem_ready.write(1)
|
self.storage_mem_ready.write(1)
|
||||||
if self.debug:
|
if self.debug:
|
||||||
print("")
|
print("")
|
||||||
if self.cd_ratio > 1:
|
|
||||||
new_data = DumpData(self.dw)
|
|
||||||
for data in self.data:
|
|
||||||
for i in range(self.cd_ratio):
|
|
||||||
new_data.append(*get_bits([data], i*self.dw, (i+1)*self.dw))
|
|
||||||
self.data = new_data
|
|
||||||
return self.data
|
return self.data
|
||||||
|
|
||||||
def save(self, filename, samplerate=None):
|
def save(self, filename, samplerate=None):
|
||||||
|
|
|
@ -70,7 +70,7 @@ class Dump:
|
||||||
values2x = [values[i//2] for i in range(len(values)*2)]
|
values2x = [values[i//2] for i in range(len(values)*2)]
|
||||||
self.add(DumpVariable(s, n, values2x))
|
self.add(DumpVariable(s, n, values2x))
|
||||||
i += n
|
i += n
|
||||||
self.add(DumpVariable("capture_clk", 1, [1, 0]*(len(self)//2)))
|
self.add(DumpVariable("scope_clk", 1, [1, 0]*(len(self)//2)))
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
l = 0
|
l = 0
|
||||||
|
|
Loading…
Reference in New Issue