commit
66956cb88f
|
@ -0,0 +1,158 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from migen import *
|
||||
|
||||
from litex.boards.platforms import arty
|
||||
from migen.genlib.io import CRG,DifferentialInput
|
||||
from litex.soc.integration.soc_core import SoCCore
|
||||
from litex.soc.cores.uart import UARTWishboneBridge
|
||||
from litex.build.generic_platform import Subsignal
|
||||
from litex.build.generic_platform import Pins
|
||||
from litex.build.generic_platform import IOStandard
|
||||
from litex.soc.cores.clock import *
|
||||
|
||||
from litescope import LiteScopeIO, LiteScopeAnalyzer
|
||||
|
||||
#
|
||||
# Use the 8 input on the dual PMOD connector B as input
|
||||
# Those are the fast and not so well protected pins.
|
||||
_serdes_io = [
|
||||
("serdes_io", 0,
|
||||
Subsignal("d0", Pins("E15"),IOStandard("LVCMOS33")),
|
||||
Subsignal("d1", Pins("E16"),IOStandard("LVCMOS33")),
|
||||
Subsignal("d2", Pins("D15"),IOStandard("LVCMOS33")),
|
||||
Subsignal("d3", Pins("C15"),IOStandard("LVCMOS33")),
|
||||
Subsignal("d4", Pins("J17"),IOStandard("LVCMOS33")),
|
||||
Subsignal("d5", Pins("J18"),IOStandard("LVCMOS33")),
|
||||
Subsignal("d6", Pins("K15"),IOStandard("LVCMOS33")),
|
||||
Subsignal("d7", Pins("J15"),IOStandard("LVCMOS33")),
|
||||
)
|
||||
]
|
||||
|
||||
class SerdesInputSignal(Module):
|
||||
def __init__(self, pad):
|
||||
|
||||
self.signals = Signal(8)
|
||||
#
|
||||
# Based on a 100MHz input clock and a 400MHz sample clock and
|
||||
# Measuring at ddr speed we are sampling at 800Mhz
|
||||
#
|
||||
self.specials += Instance("ISERDESE2",
|
||||
p_DATA_WIDTH=8, p_DATA_RATE="DDR",
|
||||
p_SERDES_MODE="MASTER", p_INTERFACE_TYPE="NETWORKING",
|
||||
p_NUM_CE=1, p_IOBDELAY="NONE",
|
||||
|
||||
i_D=pad,
|
||||
i_CE1=1,
|
||||
i_RST=ResetSignal("sys"),
|
||||
i_CLK=ClockSignal("sys4x"), i_CLKB=~ClockSignal("sys4x"),
|
||||
i_CLKDIV=ClockSignal("sys"),
|
||||
i_BITSLIP=0,
|
||||
o_Q8=self.signals[0], o_Q7=self.signals[1],
|
||||
o_Q6=self.signals[2], o_Q5=self.signals[3],
|
||||
o_Q4=self.signals[4], o_Q3=self.signals[5],
|
||||
o_Q2=self.signals[6], o_Q1=self.signals[7]
|
||||
)
|
||||
|
||||
class SerdesIO(Module):
|
||||
|
||||
def __init__(self,platform):
|
||||
platform.add_extension(_serdes_io)
|
||||
|
||||
pads = platform.request("serdes_io")
|
||||
self.submodules.d0 = SerdesInputSignal(pads.d0)
|
||||
self.submodules.d1 = SerdesInputSignal(pads.d1)
|
||||
self.submodules.d2 = SerdesInputSignal(pads.d2)
|
||||
self.submodules.d3 = SerdesInputSignal(pads.d3)
|
||||
self.submodules.d4 = SerdesInputSignal(pads.d4)
|
||||
self.submodules.d5 = SerdesInputSignal(pads.d5)
|
||||
self.submodules.d6 = SerdesInputSignal(pads.d6)
|
||||
self.submodules.d7 = SerdesInputSignal(pads.d7)
|
||||
|
||||
platform.add_platform_command("""
|
||||
set_property CFGBVS VCCO [current_design]
|
||||
set_property CONFIG_VOLTAGE 3.3 [current_design]
|
||||
""")
|
||||
|
||||
# CRG ----------------------------------------------------------------------------------------------
|
||||
|
||||
class _CRG(Module):
|
||||
def __init__(self, platform, sys_clk_freq):
|
||||
self.clock_domains.cd_sys = ClockDomain()
|
||||
self.clock_domains.cd_sys4x = ClockDomain(reset_less=True)
|
||||
|
||||
self.cd_sys.clk.attr.add("keep")
|
||||
self.cd_sys4x.clk.attr.add("keep")
|
||||
|
||||
self.submodules.pll = pll = S7PLL(speedgrade=-1)
|
||||
self.comb += pll.reset.eq(~platform.request("cpu_reset"))
|
||||
pll.register_clkin(platform.request("clk100"), 100e6)
|
||||
pll.create_clkout(self.cd_sys, sys_clk_freq)
|
||||
pll.create_clkout(self.cd_sys4x, 4*sys_clk_freq)
|
||||
|
||||
class LiteScopeSoC(SoCCore):
|
||||
csr_map = {
|
||||
"analyzer": 17
|
||||
}
|
||||
csr_map.update(SoCCore.csr_map)
|
||||
|
||||
def __init__(self, platform):
|
||||
sys_clk_freq = int(100e6)
|
||||
|
||||
SoCCore.__init__(self, platform, sys_clk_freq,
|
||||
cpu_type=None,
|
||||
csr_data_width=32,
|
||||
with_uart=False,
|
||||
ident="Fast scope", ident_version=True,
|
||||
with_timer=False
|
||||
)
|
||||
self.submodules.serdes = SerdesIO(platform)
|
||||
# crg
|
||||
self.submodules.crg = _CRG(platform,sys_clk_freq)
|
||||
|
||||
# bridge
|
||||
self.add_cpu(UARTWishboneBridge(platform.request("serial"),
|
||||
sys_clk_freq, baudrate=115200))
|
||||
self.add_wb_master(self.cpu.wishbone)
|
||||
|
||||
# Litescope Analyzer
|
||||
analyzer_groups = {}
|
||||
|
||||
# Analyzer group
|
||||
analyzer_groups[0] = [
|
||||
self.serdes.d0.signals,
|
||||
self.serdes.d1.signals,
|
||||
self.serdes.d2.signals,
|
||||
self.serdes.d3.signals,
|
||||
]
|
||||
|
||||
# analyzer
|
||||
self.submodules.analyzer = LiteScopeAnalyzer(analyzer_groups, 512)
|
||||
|
||||
def do_exit(self, vns):
|
||||
self.analyzer.export_csv(vns, "test/analyzer.csv")
|
||||
|
||||
|
||||
platform = arty.Platform()
|
||||
|
||||
soc = LiteScopeSoC(platform)
|
||||
vns = platform.build(soc)
|
||||
|
||||
#
|
||||
# Create csr and analyzer files
|
||||
#
|
||||
soc.finalize()
|
||||
csr_regions = soc.get_csr_regions()
|
||||
csr_constants = soc.get_constants()
|
||||
from litex.build.tools import write_to_file
|
||||
from litex.soc.integration import cpu_interface
|
||||
|
||||
csr_csv = cpu_interface.get_csr_csv(csr_regions, csr_constants)
|
||||
write_to_file("test/csr.csv", csr_csv)
|
||||
soc.do_exit(vns)
|
||||
|
||||
|
||||
#
|
||||
# Program
|
||||
#
|
||||
platform.create_programmer().load_bitstream("build/top.bit")
|
|
@ -0,0 +1,27 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from litex import RemoteClient
|
||||
|
||||
from litescope import LiteScopeAnalyzerDriver
|
||||
|
||||
wb = RemoteClient()
|
||||
wb.open()
|
||||
|
||||
# # #
|
||||
|
||||
subsample = 1
|
||||
analyzer = LiteScopeAnalyzerDriver(wb.regs, "analyzer", debug=True)
|
||||
analyzer.configure_subsampler(subsample)
|
||||
analyzer.configure_group(0)
|
||||
analyzer.run(offset=32, length=512)
|
||||
analyzer.wait_done()
|
||||
analyzer.upload()
|
||||
|
||||
#
|
||||
# Convert parallel input back to a flaten view (e.g. the 8 bits values are flattened)
|
||||
#
|
||||
analyzer.save("dump.vcd",flatten=True)
|
||||
|
||||
# # #
|
||||
|
||||
wb.close()
|
|
@ -126,7 +126,7 @@ class LiteScopeAnalyzerDriver:
|
|||
print("")
|
||||
return self.data
|
||||
|
||||
def save(self, filename, samplerate=None):
|
||||
def save(self, filename, samplerate=None, flatten=False):
|
||||
if self.debug:
|
||||
print("[writing to " + filename + "]...")
|
||||
name, ext = os.path.splitext(filename)
|
||||
|
@ -140,7 +140,10 @@ class LiteScopeAnalyzerDriver:
|
|||
dump = SigrokDump(samplerate=samplerate)
|
||||
else:
|
||||
raise NotImplementedError
|
||||
dump.add_from_layout(self.layouts[self.group], self.data)
|
||||
if not flatten:
|
||||
dump.add_from_layout(self.layouts[self.group], self.data)
|
||||
else:
|
||||
dump.add_from_layout_flatten(self.layouts[self.group], self.data)
|
||||
dump.write(filename)
|
||||
|
||||
def get_instant_value(self, group, name):
|
||||
|
|
|
@ -48,8 +48,8 @@ class DumpData(list):
|
|||
|
||||
class DumpVariable:
|
||||
def __init__(self, name, width, values=[]):
|
||||
self.width = width
|
||||
self.name = name
|
||||
self.width = width
|
||||
self.values = [int(v)%2**width for v in values]
|
||||
|
||||
def __len__(self):
|
||||
|
@ -64,12 +64,26 @@ class Dump:
|
|||
self.variables.append(variable)
|
||||
|
||||
def add_from_layout(self, layout, variable):
|
||||
i = 0
|
||||
for s, n in layout:
|
||||
values = variable[i:i+n]
|
||||
values2x = [values[j//2] for j in range(len(values)*2)]
|
||||
self.add(DumpVariable(s, n, values2x))
|
||||
i += n
|
||||
offset = 0
|
||||
for name, sample_width in layout:
|
||||
values = variable[offset:offset+sample_width]
|
||||
values2x = [values[i//2] for i in range(len(values)*2)]
|
||||
self.add(DumpVariable(name, sample_width, values2x))
|
||||
offset += sample_width
|
||||
self.add(DumpVariable("scope_clk", 1, [1, 0]*(len(self)//2)))
|
||||
|
||||
def add_from_layout_flatten(self, layout, variable):
|
||||
offset = 0
|
||||
for name, sample_width in layout:
|
||||
# The samples from the logic analyzer end up in an array of size sample size
|
||||
# and have n (number of channel) bits. The following does a bit slice on the array
|
||||
# elements (implemented above)
|
||||
values = variable[offset:offset+sample_width]
|
||||
values_flatten = [values[i//sample_width] >> (i % sample_width ) & 1 for i in range(len(values)*sample_width)]
|
||||
self.add(DumpVariable(name, 1, values_flatten))
|
||||
offset += sample_width
|
||||
# the clock.. might need some more love here. the clock pattern probably should be sample_width wide
|
||||
# e.g. 11110000 and not 10101010
|
||||
self.add(DumpVariable("scope_clk", 1, [1, 0]*(len(self)//2)))
|
||||
|
||||
def __len__(self):
|
||||
|
|
Loading…
Reference in New Issue