Skip to content

Commit b215df2

Browse files
SimonRenbladsbourdeauducq
authored andcommitted
comm_analyzer: add WaveformManager, WaveformChannel
1 parent 6c0ff9a commit b215df2

File tree

1 file changed

+136
-56
lines changed

1 file changed

+136
-56
lines changed

artiq/coredevice/comm_analyzer.py

+136-56
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,13 @@ class ExceptionType(Enum):
3434
i_overflow = 0b100001
3535

3636

37+
class WaveformType(Enum):
38+
ANALOG = 0
39+
BIT = 1
40+
VECTOR = 2
41+
LOG = 3
42+
43+
3744
def get_analyzer_dump(host, port=1382):
3845
sock = socket.create_connection((host, port))
3946
try:
@@ -150,6 +157,12 @@ def set_value_double(self, x):
150157
integer_cast = struct.unpack(">Q", struct.pack(">d", x))[0]
151158
self.set_value("{:064b}".format(integer_cast))
152159

160+
def set_log(self, log_message):
161+
value = ""
162+
for c in log_message:
163+
value += "{:08b}".format(ord(c))
164+
self.set_value(value)
165+
153166

154167
class VCDManager:
155168
def __init__(self, fileobj):
@@ -160,15 +173,15 @@ def __init__(self, fileobj):
160173
def set_timescale_ps(self, timescale):
161174
self.out.write("$timescale {}ps $end\n".format(round(timescale)))
162175

163-
def get_channel(self, name, width):
176+
def get_channel(self, name, width, ty):
164177
code = next(self.codes)
165178
self.out.write("$var wire {width} {code} {name} $end\n"
166179
.format(name=name, code=code, width=width))
167180
return VCDChannel(self.out, code)
168181

169182
@contextmanager
170-
def scope(self, name):
171-
self.out.write("$scope module {} $end\n".format(name))
183+
def scope(self, scope, name):
184+
self.out.write("$scope module {}/{} $end\n".format(scope, name))
172185
yield
173186
self.out.write("$upscope $end\n")
174187

@@ -177,11 +190,66 @@ def set_time(self, time):
177190
self.out.write("#{}\n".format(time))
178191
self.current_time = time
179192

193+
def set_end_time(self, time):
194+
pass
195+
196+
197+
class WaveformManager:
198+
def __init__(self):
199+
self.current_time = 0
200+
self.channels = list()
201+
self.current_scope = ""
202+
self.trace = {"timescale": 1, "stopped_x": None, "logs": dict(), "data": dict()}
203+
204+
def set_timescale_ps(self, timescale):
205+
self.trace["timescale"] = int(timescale)
206+
207+
def get_channel(self, name, width, ty):
208+
if ty == WaveformType.LOG:
209+
data = self.trace["logs"][self.current_scope + name] = list()
210+
else:
211+
data = self.trace["data"][self.current_scope + name] = list()
212+
channel = WaveformChannel(data, self.current_time)
213+
self.channels.append(channel)
214+
return channel
215+
216+
@contextmanager
217+
def scope(self, scope, name):
218+
old_scope = self.current_scope
219+
self.current_scope = scope + "/"
220+
yield
221+
self.current_scope = old_scope
222+
223+
def set_time(self, time):
224+
for channel in self.channels:
225+
channel.set_time(time)
226+
227+
def set_end_time(self, time):
228+
self.trace["stopped_x"] = time
229+
230+
231+
class WaveformChannel:
232+
def __init__(self, data, current_time):
233+
self.data = data
234+
self.current_time = current_time
235+
236+
def set_value(self, value):
237+
self.data.append((self.current_time, value))
238+
239+
def set_value_double(self, x):
240+
self.data.append((self.current_time, x))
241+
242+
def set_time(self, time):
243+
self.current_time = time
244+
245+
def set_log(self, log_message):
246+
self.data.append((self.current_time, log_message))
247+
180248

181249
class TTLHandler:
182-
def __init__(self, vcd_manager, name):
250+
def __init__(self, manager, name):
183251
self.name = name
184-
self.channel_value = vcd_manager.get_channel("ttl/" + name, 1)
252+
self.channel_value = manager.get_channel("ttl/" + name, 1, ty=WaveformType.BIT)
185253
self.last_value = "X"
186254
self.oe = True
187255

@@ -206,11 +274,11 @@ def process_message(self, message):
206274

207275

208276
class TTLClockGenHandler:
209-
def __init__(self, vcd_manager, name, ref_period):
277+
def __init__(self, manager, name, ref_period):
210278
self.name = name
211279
self.ref_period = ref_period
212-
self.channel_frequency = vcd_manager.get_channel(
213-
"ttl_clkgen/" + name, 64)
280+
self.channel_frequency = manager.get_channel(
281+
"ttl_clkgen/" + name, 64, ty=WaveformType.ANALOG)
214282

215283
def process_message(self, message):
216284
if isinstance(message, OutputMessage):
@@ -221,8 +289,8 @@ def process_message(self, message):
221289

222290

223291
class DDSHandler:
224-
def __init__(self, vcd_manager, onehot_sel, sysclk):
225-
self.vcd_manager = vcd_manager
292+
def __init__(self, manager, onehot_sel, sysclk):
293+
self.manager = manager
226294
self.onehot_sel = onehot_sel
227295
self.sysclk = sysclk
228296

@@ -231,11 +299,11 @@ def __init__(self, vcd_manager, onehot_sel, sysclk):
231299

232300
def add_dds_channel(self, name, dds_channel_nr):
233301
dds_channel = dict()
234-
with self.vcd_manager.scope("dds/{}".format(name)):
302+
with self.manager.scope("dds", name):
235303
dds_channel["vcd_frequency"] = \
236-
self.vcd_manager.get_channel(name + "/frequency", 64)
304+
self.manager.get_channel(name + "/frequency", 64, ty=WaveformType.ANALOG)
237305
dds_channel["vcd_phase"] = \
238-
self.vcd_manager.get_channel(name + "/phase", 64)
306+
self.manager.get_channel(name + "/phase", 64, ty=WaveformType.ANALOG)
239307
dds_channel["ftw"] = [None, None]
240308
dds_channel["pow"] = None
241309
self.dds_channels[dds_channel_nr] = dds_channel
@@ -285,10 +353,10 @@ def process_message(self, message):
285353

286354

287355
class WishboneHandler:
288-
def __init__(self, vcd_manager, name, read_bit):
356+
def __init__(self, manager, name, read_bit):
289357
self._reads = []
290358
self._read_bit = read_bit
291-
self.stb = vcd_manager.get_channel("{}/{}".format(name, "stb"), 1)
359+
self.stb = manager.get_channel(name + "/stb", 1, ty=WaveformType.BIT)
292360

293361
def process_message(self, message):
294362
self.stb.set_value("1")
@@ -318,16 +386,17 @@ def process_read(self, address, data, read_slack):
318386

319387

320388
class SPIMasterHandler(WishboneHandler):
321-
def __init__(self, vcd_manager, name):
389+
def __init__(self, manager, name):
322390
self.channels = {}
323-
with vcd_manager.scope("spi/{}".format(name)):
324-
super().__init__(vcd_manager, name, read_bit=0b100)
391+
self.scope = "spi"
392+
with manager.scope("spi", name):
393+
super().__init__(manager, name, read_bit=0b100)
325394
for reg_name, reg_width in [
326395
("config", 32), ("chip_select", 16),
327396
("write_length", 8), ("read_length", 8),
328397
("write", 32), ("read", 32)]:
329-
self.channels[reg_name] = vcd_manager.get_channel(
330-
"{}/{}".format(name, reg_name), reg_width)
398+
self.channels[reg_name] = manager.get_channel(
399+
"{}/{}".format(name, reg_name), reg_width, ty=WaveformType.VECTOR)
331400

332401
def process_write(self, address, data):
333402
if address == 0:
@@ -352,20 +421,21 @@ def process_read(self, address, data, read_slack):
352421

353422

354423
class SPIMaster2Handler(WishboneHandler):
355-
def __init__(self, vcd_manager, name):
424+
def __init__(self, manager, name):
356425
self._reads = []
357426
self.channels = {}
358-
with vcd_manager.scope("spi2/{}".format(name)):
359-
self.stb = vcd_manager.get_channel("{}/{}".format(name, "stb"), 1)
427+
self.scope = "spi2"
428+
with manager.scope("spi2", name):
429+
self.stb = manager.get_channel(name + "/stb", 1, ty=WaveformType.BIT)
360430
for reg_name, reg_width in [
361431
("flags", 8),
362432
("length", 5),
363433
("div", 8),
364434
("chip_select", 8),
365435
("write", 32),
366436
("read", 32)]:
367-
self.channels[reg_name] = vcd_manager.get_channel(
368-
"{}/{}".format(name, reg_name), reg_width)
437+
self.channels[reg_name] = manager.get_channel(
438+
"{}/{}".format(name, reg_name), reg_width, ty=WaveformType.VECTOR)
369439

370440
def process_message(self, message):
371441
self.stb.set_value("1")
@@ -413,27 +483,25 @@ def _extract_log_chars(data):
413483

414484

415485
class LogHandler:
416-
def __init__(self, vcd_manager, vcd_log_channels):
417-
self.vcd_channels = dict()
418-
for name, maxlength in vcd_log_channels.items():
419-
self.vcd_channels[name] = vcd_manager.get_channel("log/" + name,
420-
maxlength*8)
486+
def __init__(self, manager, log_channels):
487+
self.channels = dict()
488+
for name, maxlength in log_channels.items():
489+
self.channels[name] = manager.get_channel("logs/" + name,
490+
maxlength * 8,
491+
ty=WaveformType.LOG)
421492
self.current_entry = ""
422493

423494
def process_message(self, message):
424495
if isinstance(message, OutputMessage):
425496
self.current_entry += _extract_log_chars(message.data)
426497
if len(self.current_entry) > 1 and self.current_entry[-1] == "\x1D":
427498
channel_name, log_message = self.current_entry[:-1].split("\x1E", maxsplit=1)
428-
vcd_value = ""
429-
for c in log_message:
430-
vcd_value += "{:08b}".format(ord(c))
431-
self.vcd_channels[channel_name].set_value(vcd_value)
499+
self.channels[channel_name].set_log(log_message)
432500
self.current_entry = ""
433501

434502

435-
def get_vcd_log_channels(log_channel, messages):
436-
vcd_log_channels = dict()
503+
def get_log_channels(log_channel, messages):
504+
log_channels = dict()
437505
log_entry = ""
438506
for message in messages:
439507
if (isinstance(message, OutputMessage)
@@ -442,13 +510,13 @@ def get_vcd_log_channels(log_channel, messages):
442510
if len(log_entry) > 1 and log_entry[-1] == "\x1D":
443511
channel_name, log_message = log_entry[:-1].split("\x1E", maxsplit=1)
444512
l = len(log_message)
445-
if channel_name in vcd_log_channels:
446-
if vcd_log_channels[channel_name] < l:
447-
vcd_log_channels[channel_name] = l
513+
if channel_name in log_channels:
514+
if log_channels[channel_name] < l:
515+
log_channels[channel_name] = l
448516
else:
449-
vcd_log_channels[channel_name] = l
517+
log_channels[channel_name] = l
450518
log_entry = ""
451-
return vcd_log_channels
519+
return log_channels
452520

453521

454522
def get_single_device_argument(devices, module, cls, argument):
@@ -475,34 +543,34 @@ def get_dds_sysclk(devices):
475543
("AD9914",), "sysclk")
476544

477545

478-
def create_channel_handlers(vcd_manager, devices, ref_period,
546+
def create_channel_handlers(manager, devices, ref_period,
479547
dds_sysclk, dds_onehot_sel):
480548
channel_handlers = dict()
481549
for name, desc in sorted(devices.items(), key=itemgetter(0)):
482550
if isinstance(desc, dict) and desc["type"] == "local":
483551
if (desc["module"] == "artiq.coredevice.ttl"
484552
and desc["class"] in {"TTLOut", "TTLInOut"}):
485553
channel = desc["arguments"]["channel"]
486-
channel_handlers[channel] = TTLHandler(vcd_manager, name)
554+
channel_handlers[channel] = TTLHandler(manager, name)
487555
if (desc["module"] == "artiq.coredevice.ttl"
488556
and desc["class"] == "TTLClockGen"):
489557
channel = desc["arguments"]["channel"]
490-
channel_handlers[channel] = TTLClockGenHandler(vcd_manager, name, ref_period)
558+
channel_handlers[channel] = TTLClockGenHandler(manager, name, ref_period)
491559
if (desc["module"] == "artiq.coredevice.ad9914"
492560
and desc["class"] == "AD9914"):
493561
dds_bus_channel = desc["arguments"]["bus_channel"]
494562
dds_channel = desc["arguments"]["channel"]
495563
if dds_bus_channel in channel_handlers:
496564
dds_handler = channel_handlers[dds_bus_channel]
497565
else:
498-
dds_handler = DDSHandler(vcd_manager, dds_onehot_sel, dds_sysclk)
566+
dds_handler = DDSHandler(manager, dds_onehot_sel, dds_sysclk)
499567
channel_handlers[dds_bus_channel] = dds_handler
500568
dds_handler.add_dds_channel(name, dds_channel)
501569
if (desc["module"] == "artiq.coredevice.spi2" and
502570
desc["class"] == "SPIMaster"):
503571
channel = desc["arguments"]["channel"]
504572
channel_handlers[channel] = SPIMaster2Handler(
505-
vcd_manager, name)
573+
manager, name)
506574
return channel_handlers
507575

508576

@@ -512,11 +580,21 @@ def get_message_time(message):
512580

513581
def decoded_dump_to_vcd(fileobj, devices, dump, uniform_interval=False):
514582
vcd_manager = VCDManager(fileobj)
583+
decoded_dump_to_target(vcd_manager, devices, dump, uniform_interval)
584+
585+
586+
def decoded_dump_to_waveform_data(devices, dump, uniform_interval=False):
587+
manager = WaveformManager()
588+
decoded_dump_to_target(manager, devices, dump, uniform_interval)
589+
return manager.trace
590+
591+
592+
def decoded_dump_to_target(manager, devices, dump, uniform_interval):
515593
ref_period = get_ref_period(devices)
516594

517595
if ref_period is not None:
518596
if not uniform_interval:
519-
vcd_manager.set_timescale_ps(ref_period*1e12)
597+
manager.set_timescale_ps(ref_period*1e12)
520598
else:
521599
logger.warning("unable to determine core device ref_period")
522600
ref_period = 1e-9 # guess
@@ -526,27 +604,29 @@ def decoded_dump_to_vcd(fileobj, devices, dump, uniform_interval=False):
526604
dds_sysclk = 3e9 # guess
527605

528606
if isinstance(dump.messages[-1], StoppedMessage):
607+
m = dump.messages[-1]
608+
end_time = get_message_time(m)
529609
messages = dump.messages[:-1]
530610
else:
531611
logger.warning("StoppedMessage missing")
532612
messages = dump.messages
533613
messages = sorted(messages, key=get_message_time)
534614

535615
channel_handlers = create_channel_handlers(
536-
vcd_manager, devices, ref_period,
616+
manager, devices, ref_period,
537617
dds_sysclk, dump.dds_onehot_sel)
538-
vcd_log_channels = get_vcd_log_channels(dump.log_channel, messages)
618+
log_channels = get_log_channels(dump.log_channel, messages)
539619
channel_handlers[dump.log_channel] = LogHandler(
540-
vcd_manager, vcd_log_channels)
620+
manager, log_channels)
541621
if uniform_interval:
542622
# RTIO event timestamp in machine units
543-
timestamp = vcd_manager.get_channel("timestamp", 64)
623+
timestamp = manager.get_channel("timestamp", 64, ty=WaveformType.VECTOR)
544624
# RTIO time interval between this and the next timed event
545625
# in SI seconds
546-
interval = vcd_manager.get_channel("interval", 64)
547-
slack = vcd_manager.get_channel("rtio_slack", 64)
626+
interval = manager.get_channel("interval", 64, ty=WaveformType.ANALOG)
627+
slack = manager.get_channel("rtio_slack", 64, ty=WaveformType.ANALOG)
548628

549-
vcd_manager.set_time(0)
629+
manager.set_time(0)
550630
start_time = 0
551631
for m in messages:
552632
start_time = get_message_time(m)
@@ -560,11 +640,11 @@ def decoded_dump_to_vcd(fileobj, devices, dump, uniform_interval=False):
560640
if t >= 0:
561641
if uniform_interval:
562642
interval.set_value_double((t - t0)*ref_period)
563-
vcd_manager.set_time(i)
643+
manager.set_time(i)
564644
timestamp.set_value("{:064b}".format(t))
565645
t0 = t
566646
else:
567-
vcd_manager.set_time(t)
647+
manager.set_time(t)
568648
channel_handlers[message.channel].process_message(message)
569649
if isinstance(message, OutputMessage):
570650
slack.set_value_double(

0 commit comments

Comments
 (0)