mirror of https://github.com/m-labs/artiq.git
comm_analyzer: add WaveformManager, WaveformChannel
This commit is contained in:
parent
6c0ff9a912
commit
b215df2d25
|
@ -34,6 +34,13 @@ class ExceptionType(Enum):
|
|||
i_overflow = 0b100001
|
||||
|
||||
|
||||
class WaveformType(Enum):
|
||||
ANALOG = 0
|
||||
BIT = 1
|
||||
VECTOR = 2
|
||||
LOG = 3
|
||||
|
||||
|
||||
def get_analyzer_dump(host, port=1382):
|
||||
sock = socket.create_connection((host, port))
|
||||
try:
|
||||
|
@ -150,6 +157,12 @@ class VCDChannel:
|
|||
integer_cast = struct.unpack(">Q", struct.pack(">d", x))[0]
|
||||
self.set_value("{:064b}".format(integer_cast))
|
||||
|
||||
def set_log(self, log_message):
|
||||
value = ""
|
||||
for c in log_message:
|
||||
value += "{:08b}".format(ord(c))
|
||||
self.set_value(value)
|
||||
|
||||
|
||||
class VCDManager:
|
||||
def __init__(self, fileobj):
|
||||
|
@ -160,15 +173,15 @@ class VCDManager:
|
|||
def set_timescale_ps(self, timescale):
|
||||
self.out.write("$timescale {}ps $end\n".format(round(timescale)))
|
||||
|
||||
def get_channel(self, name, width):
|
||||
def get_channel(self, name, width, ty):
|
||||
code = next(self.codes)
|
||||
self.out.write("$var wire {width} {code} {name} $end\n"
|
||||
.format(name=name, code=code, width=width))
|
||||
return VCDChannel(self.out, code)
|
||||
|
||||
@contextmanager
|
||||
def scope(self, name):
|
||||
self.out.write("$scope module {} $end\n".format(name))
|
||||
def scope(self, scope, name):
|
||||
self.out.write("$scope module {}/{} $end\n".format(scope, name))
|
||||
yield
|
||||
self.out.write("$upscope $end\n")
|
||||
|
||||
|
@ -177,11 +190,66 @@ class VCDManager:
|
|||
self.out.write("#{}\n".format(time))
|
||||
self.current_time = time
|
||||
|
||||
def set_end_time(self, time):
|
||||
pass
|
||||
|
||||
|
||||
class WaveformManager:
|
||||
def __init__(self):
|
||||
self.current_time = 0
|
||||
self.channels = list()
|
||||
self.current_scope = ""
|
||||
self.trace = {"timescale": 1, "stopped_x": None, "logs": dict(), "data": dict()}
|
||||
|
||||
def set_timescale_ps(self, timescale):
|
||||
self.trace["timescale"] = int(timescale)
|
||||
|
||||
def get_channel(self, name, width, ty):
|
||||
if ty == WaveformType.LOG:
|
||||
data = self.trace["logs"][self.current_scope + name] = list()
|
||||
else:
|
||||
data = self.trace["data"][self.current_scope + name] = list()
|
||||
channel = WaveformChannel(data, self.current_time)
|
||||
self.channels.append(channel)
|
||||
return channel
|
||||
|
||||
@contextmanager
|
||||
def scope(self, scope, name):
|
||||
old_scope = self.current_scope
|
||||
self.current_scope = scope + "/"
|
||||
yield
|
||||
self.current_scope = old_scope
|
||||
|
||||
def set_time(self, time):
|
||||
for channel in self.channels:
|
||||
channel.set_time(time)
|
||||
|
||||
def set_end_time(self, time):
|
||||
self.trace["stopped_x"] = time
|
||||
|
||||
|
||||
class WaveformChannel:
|
||||
def __init__(self, data, current_time):
|
||||
self.data = data
|
||||
self.current_time = current_time
|
||||
|
||||
def set_value(self, value):
|
||||
self.data.append((self.current_time, value))
|
||||
|
||||
def set_value_double(self, x):
|
||||
self.data.append((self.current_time, x))
|
||||
|
||||
def set_time(self, time):
|
||||
self.current_time = time
|
||||
|
||||
def set_log(self, log_message):
|
||||
self.data.append((self.current_time, log_message))
|
||||
|
||||
|
||||
class TTLHandler:
|
||||
def __init__(self, vcd_manager, name):
|
||||
def __init__(self, manager, name):
|
||||
self.name = name
|
||||
self.channel_value = vcd_manager.get_channel("ttl/" + name, 1)
|
||||
self.channel_value = manager.get_channel("ttl/" + name, 1, ty=WaveformType.BIT)
|
||||
self.last_value = "X"
|
||||
self.oe = True
|
||||
|
||||
|
@ -206,11 +274,11 @@ class TTLHandler:
|
|||
|
||||
|
||||
class TTLClockGenHandler:
|
||||
def __init__(self, vcd_manager, name, ref_period):
|
||||
def __init__(self, manager, name, ref_period):
|
||||
self.name = name
|
||||
self.ref_period = ref_period
|
||||
self.channel_frequency = vcd_manager.get_channel(
|
||||
"ttl_clkgen/" + name, 64)
|
||||
self.channel_frequency = manager.get_channel(
|
||||
"ttl_clkgen/" + name, 64, ty=WaveformType.ANALOG)
|
||||
|
||||
def process_message(self, message):
|
||||
if isinstance(message, OutputMessage):
|
||||
|
@ -221,8 +289,8 @@ class TTLClockGenHandler:
|
|||
|
||||
|
||||
class DDSHandler:
|
||||
def __init__(self, vcd_manager, onehot_sel, sysclk):
|
||||
self.vcd_manager = vcd_manager
|
||||
def __init__(self, manager, onehot_sel, sysclk):
|
||||
self.manager = manager
|
||||
self.onehot_sel = onehot_sel
|
||||
self.sysclk = sysclk
|
||||
|
||||
|
@ -231,11 +299,11 @@ class DDSHandler:
|
|||
|
||||
def add_dds_channel(self, name, dds_channel_nr):
|
||||
dds_channel = dict()
|
||||
with self.vcd_manager.scope("dds/{}".format(name)):
|
||||
with self.manager.scope("dds", name):
|
||||
dds_channel["vcd_frequency"] = \
|
||||
self.vcd_manager.get_channel(name + "/frequency", 64)
|
||||
self.manager.get_channel(name + "/frequency", 64, ty=WaveformType.ANALOG)
|
||||
dds_channel["vcd_phase"] = \
|
||||
self.vcd_manager.get_channel(name + "/phase", 64)
|
||||
self.manager.get_channel(name + "/phase", 64, ty=WaveformType.ANALOG)
|
||||
dds_channel["ftw"] = [None, None]
|
||||
dds_channel["pow"] = None
|
||||
self.dds_channels[dds_channel_nr] = dds_channel
|
||||
|
@ -285,10 +353,10 @@ class DDSHandler:
|
|||
|
||||
|
||||
class WishboneHandler:
|
||||
def __init__(self, vcd_manager, name, read_bit):
|
||||
def __init__(self, manager, name, read_bit):
|
||||
self._reads = []
|
||||
self._read_bit = read_bit
|
||||
self.stb = vcd_manager.get_channel("{}/{}".format(name, "stb"), 1)
|
||||
self.stb = manager.get_channel(name + "/stb", 1, ty=WaveformType.BIT)
|
||||
|
||||
def process_message(self, message):
|
||||
self.stb.set_value("1")
|
||||
|
@ -318,16 +386,17 @@ class WishboneHandler:
|
|||
|
||||
|
||||
class SPIMasterHandler(WishboneHandler):
|
||||
def __init__(self, vcd_manager, name):
|
||||
def __init__(self, manager, name):
|
||||
self.channels = {}
|
||||
with vcd_manager.scope("spi/{}".format(name)):
|
||||
super().__init__(vcd_manager, name, read_bit=0b100)
|
||||
self.scope = "spi"
|
||||
with manager.scope("spi", name):
|
||||
super().__init__(manager, name, read_bit=0b100)
|
||||
for reg_name, reg_width in [
|
||||
("config", 32), ("chip_select", 16),
|
||||
("write_length", 8), ("read_length", 8),
|
||||
("write", 32), ("read", 32)]:
|
||||
self.channels[reg_name] = vcd_manager.get_channel(
|
||||
"{}/{}".format(name, reg_name), reg_width)
|
||||
self.channels[reg_name] = manager.get_channel(
|
||||
"{}/{}".format(name, reg_name), reg_width, ty=WaveformType.VECTOR)
|
||||
|
||||
def process_write(self, address, data):
|
||||
if address == 0:
|
||||
|
@ -352,11 +421,12 @@ class SPIMasterHandler(WishboneHandler):
|
|||
|
||||
|
||||
class SPIMaster2Handler(WishboneHandler):
|
||||
def __init__(self, vcd_manager, name):
|
||||
def __init__(self, manager, name):
|
||||
self._reads = []
|
||||
self.channels = {}
|
||||
with vcd_manager.scope("spi2/{}".format(name)):
|
||||
self.stb = vcd_manager.get_channel("{}/{}".format(name, "stb"), 1)
|
||||
self.scope = "spi2"
|
||||
with manager.scope("spi2", name):
|
||||
self.stb = manager.get_channel(name + "/stb", 1, ty=WaveformType.BIT)
|
||||
for reg_name, reg_width in [
|
||||
("flags", 8),
|
||||
("length", 5),
|
||||
|
@ -364,8 +434,8 @@ class SPIMaster2Handler(WishboneHandler):
|
|||
("chip_select", 8),
|
||||
("write", 32),
|
||||
("read", 32)]:
|
||||
self.channels[reg_name] = vcd_manager.get_channel(
|
||||
"{}/{}".format(name, reg_name), reg_width)
|
||||
self.channels[reg_name] = manager.get_channel(
|
||||
"{}/{}".format(name, reg_name), reg_width, ty=WaveformType.VECTOR)
|
||||
|
||||
def process_message(self, message):
|
||||
self.stb.set_value("1")
|
||||
|
@ -413,11 +483,12 @@ def _extract_log_chars(data):
|
|||
|
||||
|
||||
class LogHandler:
|
||||
def __init__(self, vcd_manager, vcd_log_channels):
|
||||
self.vcd_channels = dict()
|
||||
for name, maxlength in vcd_log_channels.items():
|
||||
self.vcd_channels[name] = vcd_manager.get_channel("log/" + name,
|
||||
maxlength*8)
|
||||
def __init__(self, manager, log_channels):
|
||||
self.channels = dict()
|
||||
for name, maxlength in log_channels.items():
|
||||
self.channels[name] = manager.get_channel("logs/" + name,
|
||||
maxlength * 8,
|
||||
ty=WaveformType.LOG)
|
||||
self.current_entry = ""
|
||||
|
||||
def process_message(self, message):
|
||||
|
@ -425,15 +496,12 @@ class LogHandler:
|
|||
self.current_entry += _extract_log_chars(message.data)
|
||||
if len(self.current_entry) > 1 and self.current_entry[-1] == "\x1D":
|
||||
channel_name, log_message = self.current_entry[:-1].split("\x1E", maxsplit=1)
|
||||
vcd_value = ""
|
||||
for c in log_message:
|
||||
vcd_value += "{:08b}".format(ord(c))
|
||||
self.vcd_channels[channel_name].set_value(vcd_value)
|
||||
self.channels[channel_name].set_log(log_message)
|
||||
self.current_entry = ""
|
||||
|
||||
|
||||
def get_vcd_log_channels(log_channel, messages):
|
||||
vcd_log_channels = dict()
|
||||
def get_log_channels(log_channel, messages):
|
||||
log_channels = dict()
|
||||
log_entry = ""
|
||||
for message in messages:
|
||||
if (isinstance(message, OutputMessage)
|
||||
|
@ -442,13 +510,13 @@ def get_vcd_log_channels(log_channel, messages):
|
|||
if len(log_entry) > 1 and log_entry[-1] == "\x1D":
|
||||
channel_name, log_message = log_entry[:-1].split("\x1E", maxsplit=1)
|
||||
l = len(log_message)
|
||||
if channel_name in vcd_log_channels:
|
||||
if vcd_log_channels[channel_name] < l:
|
||||
vcd_log_channels[channel_name] = l
|
||||
if channel_name in log_channels:
|
||||
if log_channels[channel_name] < l:
|
||||
log_channels[channel_name] = l
|
||||
else:
|
||||
vcd_log_channels[channel_name] = l
|
||||
log_channels[channel_name] = l
|
||||
log_entry = ""
|
||||
return vcd_log_channels
|
||||
return log_channels
|
||||
|
||||
|
||||
def get_single_device_argument(devices, module, cls, argument):
|
||||
|
@ -475,7 +543,7 @@ def get_dds_sysclk(devices):
|
|||
("AD9914",), "sysclk")
|
||||
|
||||
|
||||
def create_channel_handlers(vcd_manager, devices, ref_period,
|
||||
def create_channel_handlers(manager, devices, ref_period,
|
||||
dds_sysclk, dds_onehot_sel):
|
||||
channel_handlers = dict()
|
||||
for name, desc in sorted(devices.items(), key=itemgetter(0)):
|
||||
|
@ -483,11 +551,11 @@ def create_channel_handlers(vcd_manager, devices, ref_period,
|
|||
if (desc["module"] == "artiq.coredevice.ttl"
|
||||
and desc["class"] in {"TTLOut", "TTLInOut"}):
|
||||
channel = desc["arguments"]["channel"]
|
||||
channel_handlers[channel] = TTLHandler(vcd_manager, name)
|
||||
channel_handlers[channel] = TTLHandler(manager, name)
|
||||
if (desc["module"] == "artiq.coredevice.ttl"
|
||||
and desc["class"] == "TTLClockGen"):
|
||||
channel = desc["arguments"]["channel"]
|
||||
channel_handlers[channel] = TTLClockGenHandler(vcd_manager, name, ref_period)
|
||||
channel_handlers[channel] = TTLClockGenHandler(manager, name, ref_period)
|
||||
if (desc["module"] == "artiq.coredevice.ad9914"
|
||||
and desc["class"] == "AD9914"):
|
||||
dds_bus_channel = desc["arguments"]["bus_channel"]
|
||||
|
@ -495,14 +563,14 @@ def create_channel_handlers(vcd_manager, devices, ref_period,
|
|||
if dds_bus_channel in channel_handlers:
|
||||
dds_handler = channel_handlers[dds_bus_channel]
|
||||
else:
|
||||
dds_handler = DDSHandler(vcd_manager, dds_onehot_sel, dds_sysclk)
|
||||
dds_handler = DDSHandler(manager, dds_onehot_sel, dds_sysclk)
|
||||
channel_handlers[dds_bus_channel] = dds_handler
|
||||
dds_handler.add_dds_channel(name, dds_channel)
|
||||
if (desc["module"] == "artiq.coredevice.spi2" and
|
||||
desc["class"] == "SPIMaster"):
|
||||
channel = desc["arguments"]["channel"]
|
||||
channel_handlers[channel] = SPIMaster2Handler(
|
||||
vcd_manager, name)
|
||||
manager, name)
|
||||
return channel_handlers
|
||||
|
||||
|
||||
|
@ -512,11 +580,21 @@ def get_message_time(message):
|
|||
|
||||
def decoded_dump_to_vcd(fileobj, devices, dump, uniform_interval=False):
|
||||
vcd_manager = VCDManager(fileobj)
|
||||
decoded_dump_to_target(vcd_manager, devices, dump, uniform_interval)
|
||||
|
||||
|
||||
def decoded_dump_to_waveform_data(devices, dump, uniform_interval=False):
|
||||
manager = WaveformManager()
|
||||
decoded_dump_to_target(manager, devices, dump, uniform_interval)
|
||||
return manager.trace
|
||||
|
||||
|
||||
def decoded_dump_to_target(manager, devices, dump, uniform_interval):
|
||||
ref_period = get_ref_period(devices)
|
||||
|
||||
if ref_period is not None:
|
||||
if not uniform_interval:
|
||||
vcd_manager.set_timescale_ps(ref_period*1e12)
|
||||
manager.set_timescale_ps(ref_period*1e12)
|
||||
else:
|
||||
logger.warning("unable to determine core device ref_period")
|
||||
ref_period = 1e-9 # guess
|
||||
|
@ -526,6 +604,8 @@ def decoded_dump_to_vcd(fileobj, devices, dump, uniform_interval=False):
|
|||
dds_sysclk = 3e9 # guess
|
||||
|
||||
if isinstance(dump.messages[-1], StoppedMessage):
|
||||
m = dump.messages[-1]
|
||||
end_time = get_message_time(m)
|
||||
messages = dump.messages[:-1]
|
||||
else:
|
||||
logger.warning("StoppedMessage missing")
|
||||
|
@ -533,20 +613,20 @@ def decoded_dump_to_vcd(fileobj, devices, dump, uniform_interval=False):
|
|||
messages = sorted(messages, key=get_message_time)
|
||||
|
||||
channel_handlers = create_channel_handlers(
|
||||
vcd_manager, devices, ref_period,
|
||||
manager, devices, ref_period,
|
||||
dds_sysclk, dump.dds_onehot_sel)
|
||||
vcd_log_channels = get_vcd_log_channels(dump.log_channel, messages)
|
||||
log_channels = get_log_channels(dump.log_channel, messages)
|
||||
channel_handlers[dump.log_channel] = LogHandler(
|
||||
vcd_manager, vcd_log_channels)
|
||||
manager, log_channels)
|
||||
if uniform_interval:
|
||||
# RTIO event timestamp in machine units
|
||||
timestamp = vcd_manager.get_channel("timestamp", 64)
|
||||
timestamp = manager.get_channel("timestamp", 64, ty=WaveformType.VECTOR)
|
||||
# RTIO time interval between this and the next timed event
|
||||
# in SI seconds
|
||||
interval = vcd_manager.get_channel("interval", 64)
|
||||
slack = vcd_manager.get_channel("rtio_slack", 64)
|
||||
interval = manager.get_channel("interval", 64, ty=WaveformType.ANALOG)
|
||||
slack = manager.get_channel("rtio_slack", 64, ty=WaveformType.ANALOG)
|
||||
|
||||
vcd_manager.set_time(0)
|
||||
manager.set_time(0)
|
||||
start_time = 0
|
||||
for m in messages:
|
||||
start_time = get_message_time(m)
|
||||
|
@ -560,11 +640,11 @@ def decoded_dump_to_vcd(fileobj, devices, dump, uniform_interval=False):
|
|||
if t >= 0:
|
||||
if uniform_interval:
|
||||
interval.set_value_double((t - t0)*ref_period)
|
||||
vcd_manager.set_time(i)
|
||||
manager.set_time(i)
|
||||
timestamp.set_value("{:064b}".format(t))
|
||||
t0 = t
|
||||
else:
|
||||
vcd_manager.set_time(t)
|
||||
manager.set_time(t)
|
||||
channel_handlers[message.channel].process_message(message)
|
||||
if isinstance(message, OutputMessage):
|
||||
slack.set_value_double(
|
||||
|
|
Loading…
Reference in New Issue