DUNE-DAQ
DUNE Trigger and Data Acquisition software
Loading...
Searching...
No Matches
hermesbutler.py
Go to the documentation of this file.
1#!/usr/bin/env python
2
3import click
4import os
5import socket
6import time
7import uhal
8import logging
9from rich import print
10from rich.table import Table
11from rich.logging import RichHandler
12
13from hermesmodules.tx_endpoints import tx_endpoints
14from hermesmodules.rx_endpoints import rx_endpoints
15
16from pprint import pprint
17
18
19# from crappyhalclient import CrappyHardwareClient
20
21# -----------------------------------------------------------------------------
22# Utilities
23def dict_to_hextable( vals: dict, **kwargs):
24
25 t = Table(**kwargs)
26 t.add_column('name')
27 t.add_column('value', style='green')
28 for k,v in vals.items():
29 t.add_row(k,hex(v))
30 return t
31
32def dict_to_table( vals: dict, **kwargs):
33
34 t = Table(**kwargs)
35 t.add_column('name')
36 t.add_column('value', style='green')
37 for k,v in vals.items():
38 t.add_row(k,str(v))
39 return t
40
41def read_regs(hw, reg_list):
42 d = {}
43 for r in reg_list:
44 v = hw.read(r)
45 d[r] = v
46 return d
47
48def read_and_print(hw, reg_list):
49 d = {}
50 for r in reg_list:
51 v = hw.read(r)
52 d[r] = v
53 print(dict_to_hextable(d))
54
55
56# -----------------------------------------------------------------------------
57def dump_sub_regs(node):
58 regs = {}
59 for i in sorted(node.getNodes()):
60 regs[i] = node.getNode(i).read()
61 node.getClient().dispatch()
62
63 return {k: v.value() for k, v in regs.items()}
64
65
66# -----------------------------------------------------------------------------
67def dump_reg(node):
68 v = node.read()
69 node.getClient().dispatch()
70 return {node.getId(): v.value()}
71
72
74 def __init__(self, node):
75 self.node = node
76
77 self._load_info()
78
79
80 def _load_info(self):
81 magic = self.node.getNode('info.magic').read()
82 self.node.getClient().dispatch()
83 self.magic = magic.value()
84 if self.magic != 0xdeadbeef:
85 raise ValueError(f"Magic number check failed. Expected '0xdeadbeef', found '{hex(self.magic)}'")
86
87
88
89 n_mgt = self.node.getNode('info.generics.n_mgts').read()
90 n_src = self.node.getNode('info.generics.n_srcs').read()
91 ref_freq = self.node.getNode('info.generics.ref_freq').read()
92 self.node.getClient().dispatch()
93
94 # Generics
95 self.n_mgt = n_mgt.value()
96 self.n_src = n_src.value()
97 self.ref_freq = ref_freq.value()
98 # Extra info
99 self.n_srcs_p_mgt = self.n_src
100
101 def get_node(self, id):
102 return self.node.getNode(id)
103
104 def get_nodes(self, regex):
105 return self.node.getNodes(regex)
106
107 def dispatch(self):
108 self.node.getClient().dispatch()
109
110 def sample_ctrs(self, seconds: int):
111
112 if not seconds:
113 self.node.getNode('samp.ctrl.samp').write(True)
114 self.node.getNode('samp.ctrl.samp').write(False)
115 self.node.getClient().dispatch()
116 else:
117 self.node.getNode('samp.ctrl.samp').write(True)
118 self.node.getClient().dispatch()
119 time.sleep(seconds)
120 self.node.getNode('samp.ctrl.samp').write(False)
121 self.node.getClient().dispatch()
122
123
124
125 def sel_tx_mux(self, i: int):
126
127 if i >= self.n_mgt:
128 raise ValueError(f"Link {i} does not exist ({self.n_mgt})")
129
130 self.get_node('tx_path.csr_tx_mux.ctrl.tx_mux_sel').write(i)
131 self.dispatch()
132 j = self.get_node('tx_path.csr_tx_mux.ctrl.tx_mux_sel').read()
133 self.dispatch()
134 print(f"Link {j} selected")
135
136
137 def sel_tx_mux_buf(self, i: int):
138
139 if i >= self.n_src:
140 raise ValueError(f"Input buffer {i} does not exist ({self.n_src})")
141
142 self.node.getNode('tx_path.tx_mux.csr.ctrl.sel_buf').write(i)
143 self.node.getClient().dispatch()
144
145 def sel_udp_core(self, i: int):
146
147 if i >= self.n_mgt:
148 raise ValueError(f"Link {i} does not exist ({self.n_mgt})")
149
150 self.node.getNode('tx_path.csr_udp_core.ctrl.udp_core_sel').write(i)
151 self.node.getClient().dispatch()
152
153
154# N_MGT=4
155# N_SRC=8
156# N_SRCS_P_MGT = N_SRC//N_MGT
157MAX_MGT=4
158MAX_SRCS_P_MGT =16
159mgts_all = tuple(str(i) for i in range(MAX_MGT))
160#
161CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help'])
162
163# DEFAULT_MAP = {'connections': '${HERMESMODULES_SHARE}/config/etc/connections.xml'}
164# CONTEXT_SETTINGS = {
165# 'help_option_names': ['-h', '--help'],
166# 'default_map': DEFAULT_MAP,
167# }
168
169# -----------------
170def validate_device(ctx, param, value):
171 lDevices = ctx.obj.cm.getDevices()
172 if value and (value not in lDevices):
173 raise click.BadParameter(
174 'Device must be one of '+
175 ', '.join(["'"+lId+"'" for lId in lDevices])
176 )
177 return value
178# -----------------
179
181
182 def __init__(self):
183 uhal.setLogLevelTo(uhal.LogLevel.WARNING)
184 self._cm_cm = None
185 self.hw = None
186 self.device_id = None
187 self.connection_file_path = 'file://${HERMESMODULES_SHARE}/config/c.xml'
188 self._controller = None
189
191 if not self.device_id:
192 raise ValueError('the option --device/-d must be provided to run this command. for a list of available device names you can provide, run "hermesbutler.py addrbook".')
193
194 @property
195 def hermes(self):
196 if self._controller is None:
197 hw = self.cm.getDevice(self.device_id)
198
199 # Identify board
200 is_zcu = hw.getNodes('tx.info')
201 is_detector = hw.getNodes('info')
202
203 if is_zcu:
204 print("zcu mode")
205 tx_mux = hw.getNode('tx')
206 elif is_detector:
207 print("detector mode")
208 tx_mux = hw.getNode()
209 else:
210 raise ValueError(f"{self.device_id} is neither a zcu nor a detector board")
211
212 self.hw = hw
213 # pprint(vars(self))
214 self._controller = HermesModule(tx_mux)
215
216 return self._controller
217
218 @property
219 def cm(self):
220 if self._cm_cm is None:
221 self._cm_cm = uhal.ConnectionManager(self.connection_file_path)
222
223 return self._cm_cm
224
225
226@click.group(chain=True, context_settings=CONTEXT_SETTINGS)
227@click.option('-c', '--connection-file', default=None, help="IPBus connection file ")
228@click.option('-d', '--device', callback=validate_device, help="IPBus device")
229# @click.pass_context
230@click.pass_obj
231def cli(obj, connection_file, device):
232 obj.connection_file_path = connection_file
233 obj.device_id = device
234
235@cli.command()
236@click.pass_obj
237def addrbook(obj):
238
239 t = Table(title="Control hosts")
240 t.add_column('name')
241 # t.add_column('addrtable', style='green')
242 for h in obj.cm.getDevices():
243 t.add_row(h)
244 print(t)
245
246 t = Table(title="Receivers")
247 t.add_column('name')
248 t.add_column('mac', style='green')
249 t.add_column('ip', style='blue')
250 t.add_column('port', style='blue')
251 for h,d in rx_endpoints.items():
252 t.add_row(h, f"0x{d['mac']:012x}", f"{d['ip']}", str(d['port']))
253 print(t)
254
255 t = Table(title="Transmitters")
256 t.add_column('name')
257 t.add_column('mac', style='green')
258 t.add_column('ip', style='blue')
259 t.add_column('port', style='blue')
260 for h,d in tx_endpoints.items():
261 t.add_row(h, f"0x{d['mac']:012x}", f"{d['ip']}", str(d['port']))
262 print(t)
263
264
265@cli.command()
266@click.option('--nuke', is_flag=True, default=None)
267@click.pass_obj
268def reset(obj, nuke):
269 obj.check_device_id() # ensure the device ID option was provided by the user.
270 hrms = obj.hermes
271
272 if nuke is not None:
273 hrms.get_node('csr.ctrl.nuke').write(0x1)
274 hrms.dispatch()
275
276 time.sleep(0.1)
277
278 hrms.get_node('csr.ctrl.nuke').write(0x0)
279 hrms.dispatch()
280
281 hrms.get_node('csr.ctrl.soft_rst').write(0x1)
282 hrms.dispatch()
283
284 time.sleep(0.1)
285
286 hrms.get_node('csr.ctrl.soft_rst').write(0x0)
287 hrms.dispatch()
288
289
290@cli.command()
291@click.option('--en/--dis', 'enable', default=None)
292@click.option('--buf-en/--buf-dis', 'buf_en', default=None)
293@click.option('--tx-en/--tx-dis', 'tx_en', default=None)
294@click.option('-l', '--link', type=int, default=0)
295@click.pass_obj
296def enable(obj, enable, buf_en, tx_en, link):
297
298 obj.check_device_id() # ensure the device ID option was provided by the user.
299 hrms = obj.hermes
300
301 n_mgt = hrms.n_mgt
302
303 if link >= n_mgt:
304 raise ValueError(f"MGT {link} not instantiated")
305
306 hrms.sel_tx_mux(link)
307
308 print()
309
310 tx_en = tx_en if tx_en is not None else enable
311 buf_en = buf_en if buf_en is not None else enable
312
313 if tx_en is not None:
314 print(f"- {'Enabling' if tx_en else 'Disabling'} 'tx block'")
315 hrms.get_node('tx_path.tx_mux.csr.ctrl.tx_en').write(tx_en)
316 print()
317
318 if buf_en is not None:
319 print(f"- {'Enabling' if buf_en else 'Disabling'} 'input buffers'")
320 hrms.get_node('tx_path.tx_mux.csr.ctrl.en_buf').write(buf_en)
321
322 time.sleep(0.1)
323
324 if enable is not None:
325 print(f"- {'Enabling' if enable else 'Disabling'} 'mux'")
326 hrms.get_node('tx_path.tx_mux.csr.ctrl.en').write(enable)
327
328
329
330 hrms.dispatch()
331
332
333 top_ctrl = dump_sub_regs(hrms.get_node('tx_path.tx_mux.csr.ctrl'))
334
335 print(
336 dict_to_hextable(top_ctrl, title=f'Link {link} ctrls', show_header=False),
337 )
338
339
340@cli.command("mux-config")
341@click.argument('detid', type=int)
342@click.argument('crate', type=int)
343@click.argument('slot', type=int)
344@click.option('-l', '--link', type=int, default=0)
345@click.pass_obj
346def mux_config(obj, detid, crate, slot, link):
347 """Configure the UDP blocks """
348
349 obj.check_device_id() # ensure the device ID option was provided by the user.
350 hrms = obj.hermes
351
352 hrms.sel_tx_mux(link)
353
354 hrms.get_node('tx_path.tx_mux.mux.ctrl.detid').write(detid)
355 hrms.get_node('tx_path.tx_mux.mux.ctrl.crate').write(crate)
356 hrms.get_node('tx_path.tx_mux.mux.ctrl.slot').write(slot)
357
358 mux_ctrl = dump_sub_regs(hrms.get_node('tx_path.tx_mux.mux.ctrl'))
359
360 print(
361 dict_to_hextable(mux_ctrl, title=f'Link {link} mux cfg', show_header=False),
362 )
363
364
365@cli.command("udp-config")
366@click.argument('src_id', type=click.Choice(tx_endpoints.keys()))
367@click.argument('dst_id', type=click.Choice(rx_endpoints.keys()))
368@click.option('-l', '--link', type=int, default=0)
369@click.pass_obj
370def udp_config(obj, src_id, dst_id, link):
371 """Configure the UDP blocks """
372
373 obj.check_device_id() # ensure the device ID option was provided by the user.
374 filter_control = 0x07400307
375 hrms = obj.hermes
376
377 if link >= hrms.n_mgt:
378 raise ValueError(f"Link {link} not instantiated")
379
380 hrms.sel_udp_core(link)
381
382 dst = rx_endpoints[dst_id]
383 src = tx_endpoints[src_id]
384
385 udp_core_ctrl = f'tx_path.udp_core.udp_core_control'
386
387 hrms.get_node(f'{udp_core_ctrl}.ctrl.filter_control').write(filter_control)
388
389 hrms.get_node(f'{udp_core_ctrl}.src_addr_ctrl.use_external').write(0);
390
391 # Our IP address = 10.73.139.23
392 # print(f"Our ip address: {socket.inet_ntoa(src['ip'].to_bytes(4, 'big'))}")
393 src_u32 = int.from_bytes(socket.inet_aton(src['ip']),"big")
394 print(f"Our ip address: {src['ip']} (0x{src_u32:08x})")
395
396 hrms.get_node(f'{udp_core_ctrl}.src_addr_ctrl.src_ip_addr').write(src_u32)
397
398 # Their IP address = 10.73.139.23
399 # print(f"Their ip address: {socket.inet_ntoa(dst['ip'].to_bytes(4, 'big'))}")
400 dst_u32 = int.from_bytes(socket.inet_aton(dst['ip']),"big")
401 print(f"Their ip address: {dst['ip']} (0x{dst_u32:08x})")
402 hrms.get_node(f'{udp_core_ctrl}.ctrl.dst_ip_addr').write(dst_u32)
403
404 # Our MAC address
405 # Dest MAC address
406 print(f"Our mac address: 0x{src['mac']:012x}")
407 hrms.get_node(f'{udp_core_ctrl}.src_addr_ctrl.src_mac_addr_lower').write(src['mac'] & 0xffffffff)
408 hrms.get_node(f'{udp_core_ctrl}.src_addr_ctrl.src_mac_addr_upper').write((src['mac'] >> 32) & 0xffff)
409
410 # Dest MAC address
411 print(f"Their mac address: 0x{dst['mac']:012x}")
412 hrms.get_node(f'{udp_core_ctrl}.ctrl.dst_mac_addr_lower').write(dst['mac'] & 0xffffffff)
413 hrms.get_node(f'{udp_core_ctrl}.ctrl.dst_mac_addr_upper').write((dst['mac'] >> 32) & 0xffff)
414
415 # Ports
416 hrms.get_node(f'{udp_core_ctrl}.src_addr_ctrl.src_port').write(src['port'])
417 hrms.get_node(f'{udp_core_ctrl}.ctrl.dst_port').write(dst['port'])
418
419 hrms.dispatch()
420
421
422@cli.command("zcu-src-config")
423@click.option('-l', '--link', type=int, default=0)
424@click.option('-n', '--en-n-src', type=click.IntRange(0, MAX_SRCS_P_MGT), default=1)
425@click.option('-d', '--dlen', type=click.IntRange(0, 0xfff), default=0x383)
426@click.option('-r', '--rate-rdx', type=click.IntRange(0, 0x3f), default=0xa)
427@click.pass_obj
428def zcu_src_config(obj, link, en_n_src, dlen, rate_rdx):
429 """Configure trivial data sources"""
430
431 obj.check_device_id() # ensure the device ID option was provided by the user.
432 hw = obj.cm.getDevice(obj.device_id)
433 #hw = obj.hw
434 hrms = obj.hermes
435
436 # n_mgt = obj.n_mgt
437 # n_src = obj.n_src
438 # n_srcs_p_mgt = n_src//n_mgt
439
440 #print(hw)
441 #pprint(vars(obj))
442 #pprint(vars(hrms))
443 #print(hw)
444
445 if link >= hrms.n_mgt:
446 raise ValueError(f"MGT {link} not instantiated")
447
448 if en_n_src > hrms.n_srcs_p_mgt:
449 raise ValueError(f"{en_n_src} must be lower than the number of generators per link ({hrms.n_srcs_p_mgt})")
450
451 for i in range(hrms.n_srcs_p_mgt):
452 src_id = hrms.n_srcs_p_mgt*link+i
453 hw.getNode('src.csr.ctrl.sel').write(src_id)
454 src_en = (i<en_n_src)
455 print(f'Configuring generator {src_id} : {src_en}')
456 #data_gen = hw.getNode("src.data_src.src.params.write")
457 #data_gen.getNode("gap").write(1)
458 #hw.getNode('src.data_src.src.csr.ctrl.start_stop').write(src_en)
459 #if not src_en:
460 # continue
461
465 hw.dispatch()
466
467
468 regs = {}
469 for i in range(hrms.n_srcs_p_mgt):
470 hw.getNode('src.csr.ctrl.sel').write(i)
471 src_regs = dump_sub_regs(hw.getNode('src.csr'))
472 data_src_regs = dump_sub_regs(hw.getNode('src.data_src'))
473
474 grid = Table.grid()
475 grid.add_column("ctrl")
476 grid.add_column("stat")
477 grid.add_row(
478 dict_to_hextable(src_regs, title='src_ctrl', show_header=False),
479 #dict_to_hextable(data_src_regs, title='data_src_ctrl', show_header=False)
480 )
481 for j in range(hrms.n_srcs_p_mgt):
482 grid.add_row(
483 dict_to_hextable(data_src_regs, title='data_src_ctrl', show_header=False)
484
485 )
486 print(f'Link {i}')
487 print(grid)
488
489 #regs[i] = dump_sub_regs(hw.getNode('src.csr.stat.no_of_src_blocks'))
490
491 # Create the summary table
492 t = Table()
493
494
495
496 # Add 1 column for the reg name, and as many as the number of sources
497 #t.add_column('name')
498 # for j in range(hrms.n_srcs_p_mgt):
499 # t.add_column(f'Src {j}', style='green')
500
501 # for n in hw.getNode('src').getNodes():
502 # t.add_row(n, *[hex(regs[i][n]) for i in range(hrms.n_srcs_p_mgt)])
503
504
505 #print(t)
506
507@cli.command("fakesrc-config")
508@click.option('-l', '--link', type=int, default=0)
509@click.option('-n', '--n-src', type=click.IntRange(0, MAX_SRCS_P_MGT), default=1)
510@click.option('-s', '--src', type=click.IntRange(0, MAX_SRCS_P_MGT), default=None, multiple=True)
511@click.option('-k', '--data-len', type=click.IntRange(0, 0xfff), default=0x383)
512@click.option('-r', '--rate-rdx', type=click.IntRange(0, 0x3f), default=0xa)
513@click.pass_obj
514def fakesrc_config(obj, link, n_src, src, data_len, rate_rdx):
515 """Configure trivial data sources"""
516
517 obj.check_device_id() # ensure the device ID option was provided by the user.
518 hrms = obj.hermes
519
520 all_srcs = set(range(hrms.n_srcs_p_mgt))
521 en_srcs = set(src)
522
523 # print(all_srcs)
524 # print(en_srcs)
525
526 if en_srcs != set.intersection(all_srcs, en_srcs):
527 raise ValueError("AAARGH")
528
529 # print(src)
530 # return
531
532 hrms.sel_tx_mux(link)
533
534 if n_src > hrms.n_srcs_p_mgt:
535 raise ValueError(f"{n_src} must be lower than the number of generators per link ({hrms.n_srcs_p_mgt})")
536
537 was_en = hrms.get_node('tx_path.tx_mux.csr.ctrl.en_buf').read()
538 # disable buffer before reconfiguring "or bad things will happen"
539 hrms.get_node('tx_path.tx_mux.csr.ctrl.en_buf').write(0x0)
540 hrms.dispatch()
541
542 for src_id in range(hrms.n_srcs_p_mgt):
543 hrms.sel_tx_mux_buf(src_id)
544
545 src_en = (src_id in en_srcs)
546 print(f'Configuring generator {src_id} : {src_en}')
547 # hw.write(f'tx.buf.ctrl.fake_en', src_en)
548 hrms.get_node('tx_path.tx_mux.buf.ctrl.fake_en').write(src_en)
549 if not src_en:
550 continue
551
552 hrms.get_node('tx_path.tx_mux.buf.ctrl.dlen').write(data_len)
553
554 hrms.get_node('tx_path.tx_mux.buf.ctrl.rate_rdx').write(rate_rdx)
555 hrms.dispatch()
556
557 hrms.get_node('tx_path.tx_mux.csr.ctrl.en_buf').write(was_en.value())
558 hrms.dispatch()
559
560
561@cli.command()
562@click.pass_obj
563@click.option('-l', '--links', 'sel_links', type=click.Choice(mgts_all), multiple=True, default=None)
564@click.option('-s', '--seconds', type=int, default=0)
565@click.option('-u/-U', '--show-udp/--hide-udp', 'show_udp', default=True)
566@click.option('-b/-B', '--show-buf/--hide-buf', 'show_buf', default=True)
567
568def stats(obj, sel_links, seconds, show_udp, show_buf):
569 """Simple program that greets NAME for a total of COUNT times."""
570
571 obj.check_device_id() # ensure the device ID option was provided by the user.
572 hrms = obj.hermes
573
574 mgts = list(range(hrms.n_mgt))
575
576 # deal with defaults
577 if not sel_links:
578 sel_links = mgts
579 else:
580 sel_links = [int(s) for s in sel_links]
581
582
583 # Check for existance
584 if not set(sel_links).issubset(mgts):
585 print(sel_links, mgts)
586 raise ValueError(f"MGTs {set(sel_links)-set(mgts)} are not instantiated")
587
588 # mgts = [int(s) for s in (mgts if mgts else [0])]
589
590 print(f"Sampling hermes counters for {seconds}s")
591 hrms.sample_ctrs(seconds)
592
593 ts_l = hrms.get_node('samp.samp_ts_l').read()
594 ts_h = hrms.get_node('samp.samp_ts_h').read()
595 hrms.dispatch()
596
597 print(f"Current timestamp : {(ts_h.value() << 32) + ts_l.value()}")
598
599 info_data = dump_sub_regs(hrms.get_node('info'))
600 print(dict_to_hextable(info_data, title='hermes info', show_header=False))
601
602 # n_srcs_p_mgt = n_src//n_mgt
603
604 for i in sel_links:
605 print(f'---Tx Mux {i} Status---')
606
607 hrms.sel_tx_mux(i)
608
609
610 # cli control registers
611 top_ctrl = dump_sub_regs(hrms.get_node('tx_path.tx_mux.csr.ctrl'))
612 top_stat = dump_sub_regs(hrms.get_node('tx_path.tx_mux.csr.stat'))
613 ctrl_mux = dump_sub_regs(hrms.get_node('tx_path.tx_mux.mux.ctrl'))
614 stat_mux = dump_sub_regs(hrms.get_node('tx_path.tx_mux.mux.stat'))
615
616 grid = Table.grid()
617 grid.add_column("ctrl")
618 grid.add_column("stat")
619 grid.add_row(
620 dict_to_hextable(top_ctrl, title='tx_mux ctrl', show_header=False),
621 dict_to_hextable(top_stat, title='tx mux stat', show_header=False),
622 dict_to_hextable(ctrl_mux, title="mux ctrl", show_header=False),
623 dict_to_hextable(stat_mux, title="mux stat", show_header=False),
624 )
625 print(grid)
626
627
628 if show_udp:
629 hrms.sel_udp_core(i)
630 ctrl_udp_src = dump_sub_regs(hrms.get_node(f'tx_path.udp_core.udp_core_control.src_addr_ctrl'))
631 ctrl_udp = dump_sub_regs(hrms.get_node(f'tx_path.udp_core.udp_core_control.ctrl'))
632 ctrl_flt_udp = dump_sub_regs(hrms.get_node(f'tx_path.udp_core.udp_core_control.ctrl.filter_control'))
633 if hrms.get_nodes(f'tx_path.udp_core.udp_core_control.rx_packet_counters'):
634 print("New tx counters found")
635 stat_rx_udp = dump_sub_regs(hrms.get_node(f'tx_path.udp_core.udp_core_control.rx_packet_counters'))
636 stat_tx_udp = dump_sub_regs(hrms.get_node(f'tx_path.udp_core.udp_core_control.tx_packet_counters'))
637 else:
638 print("No new tx counters found")
639 stat_rx_udp = dump_sub_regs(hrms.get_node(f'tx_path.udp_core.udp_core_control.tx_packet_counters'))
640 stat_tx_udp = {'-':0}
641
642
643 ctrl_srcdst = {}
644 ctrl_srcdst['src_ip'] = ctrl_udp_src['src_ip_addr']
645 ctrl_srcdst['dst_ip'] = ctrl_udp['dst_ip_addr']
646 ctrl_srcdst['src_mac'] = (ctrl_udp_src['src_mac_addr_upper'] << 32) + ctrl_udp_src['src_mac_addr_lower']
647 ctrl_srcdst['dst_mac'] = (ctrl_udp['dst_mac_addr_upper'] << 32) + ctrl_udp['dst_mac_addr_lower']
648 ctrl_srcdst['src_port'] = ctrl_udp_src['src_port']
649 ctrl_srcdst['dst_port'] = ctrl_udp['dst_port']
650
651 grid = Table.grid()
652 grid.add_column("ctrl")
653 grid.add_column("stat")
654 grid.add_row(
655 # dict_to_hextable(ctrl_udp, title="udp ctrl", show_header=False),
656 dict_to_hextable(ctrl_srcdst, title="udp src/dst", show_header=False),
657 dict_to_hextable(ctrl_flt_udp, title="udp filter", show_header=False),
658 dict_to_hextable(stat_rx_udp, title="udp rx stat", show_header=False),
659 dict_to_hextable(stat_tx_udp, title="udp tx stat", show_header=False),
660 )
661 print(grid)
662
663 if show_buf:
664 ibuf_stats = {}
665
666 src_ids = tuple(range(hrms.n_srcs_p_mgt))
667 for j in src_ids:
668 # hw.write('tx.mux.csr.ctrl.sel_buf',j)
669 hrms.sel_tx_mux_buf(j)
670 s = dump_sub_regs(hrms.get_node('tx_path.tx_mux.buf'))
671 s['blk_acc'] = (s['blk_acc_h']<<32)+s['blk_acc_l']
672 s['blk_oflow'] = (s['blk_oflow_h']<<32)+s['blk_oflow_l']
673 s['blk_rej'] = (s['blk_rej_h']<<32)+s['blk_rej_l']
674 s['ts'] = (s['ts_h']<<32)+s['ts_l']
675 s['vol'] = (s['vol_h']<<32)+s['vol_l']
676 s['blk_longlast'] = (s['blk_longlast_h']<<32)+s['blk_longlast_l']
677 s['blk_lastnotval'] = (s['blk_lastnotval_h']<<32)+s['blk_lastnotval_l']
678
679 for k in tuple(s.keys()):
680 for n in ('blk_acc_', 'blk_oflow_', 'blk_rej_', 'ts_', 'vol_', 'blk_longlast_', 'blk_lastnotval_'):
681 if k.startswith(n):
682 del s[k]
683
684 for k in ('ctrl', 'stat', 'buf_mon'):
685 del s[k]
686
687
688 ibuf_stats[j] = s
689
690 # Create the summary table
691 t = Table()
692
693 # Add 1 column for the reg name, and as many as the number of sources
694 t.add_column('name')
695 for j in src_ids:
696 t.add_column(f'Buf {j}', style='green')
697
698 # Unify the reg list (useless?)
699 reg_names = set()
700 for k,v in ibuf_stats.items():
701 reg_names = reg_names.union(v.keys())
702
703 for n in sorted(reg_names):
704 t.add_row(n,*(hex(ibuf_stats[j][n]) for j in src_ids))
705 print(t)
706
707
708@cli.command()
709@click.pass_obj
710@click.option('-r', '--phy-reset', is_flag=True, default=None, help="Reset the phy block")
711def tx_path(obj, phy_reset):
712 '''
713 TX path low-level controls
714 '''
715
716 obj.check_device_id() # ensure the device ID option was provided by the user.
717 hrms = obj.hermes
718 pcs_pma_node = hrms.get_node('pcs_pma')
719
720 if phy_reset:
721 print("[cyan]Resetting phy[/cyan]")
722 pcs_pma_node.getNode('debug.csr.ctrl.phy_reset').write(0x1)
723 pcs_pma_node.getNode('debug.csr.ctrl.phy_reset').write(0x0)
724 pcs_pma_node.getClient().dispatch()
725 print("[cyan]Phy reset completed[/cyan]")
726
727
728@cli.command()
729@click.pass_obj
731 '''
732 TX path clock monitoring
733 '''
734 clk_ids = [
735 'ref_clk_out',
736 'tx_mii_clk_0',
737 'rx_clk_out_0',
738 ]
739
740 obj.check_device_id() # ensure the device ID option was provided by the user.
741 hrms = obj.hermes
742 pcs_pma_node = hrms.get_node('pcs_pma')
743
744 # print(pcs_pma_node.getNode('debug.csr.stat').getNodes())
745 stats = { n: pcs_pma_node.getNode('debug.csr.stat.'+n).read() for n in pcs_pma_node.getNode('debug.csr.stat').getNodes() }
746 pcs_pma_node.getClient().dispatch()
747
748 print(dict_to_hextable(stats, title='TX clock registers', show_header=False))
749
750
751 clk_freqs = {}
752 for idx, clk_id in enumerate(clk_ids):
753
754 fn = pcs_pma_node.getNode('freq')
755 fn.getNode('ctrl.chan_sel').write(idx)
756 fn.getNode('ctrl.en_crap_mode').write(0x0)
757 fn.getClient().dispatch()
758
759 time.sleep(1.1)
760 c = fn.getNode('freq.count').read()
761 v = fn.getNode('freq.valid').read()
762 fn.getClient().dispatch()
763
764 if v == 0:
765 print(f'[red]Failed to measure frequency of clock {clk_id}[/red]')
766
767 ipb_freq = 100000000
768 # ipb_freq = 125000000
769 denominator = 2**24
770 true_count = int(c)*64
771
772 clk_freq = true_count/denominator*ipb_freq
773 # print(f"Measured {clk_id} clock freq {clk_freq/1000000:04} MHz")
774 print(f"Measured clock '{clk_id}'")
775 clk_freqs[clk_id] = clk_freq
776
777
778 print(dict_to_table({k:f"{v/1000000:7.3f} MHz" for k,v in clk_freqs.items()}, title='TX clocks', show_header=False))
779
780
781
782
783
784if __name__ == '__main__':
785 FORMAT = "%(message)s"
786 logging.basicConfig(
787 level="INFO", format=FORMAT, datefmt="[%X]", handlers=[RichHandler()]
788 )
789 cli(obj=HermesCliObj())
sample_ctrs(self, int seconds)
dict_to_hextable(dict vals, **kwargs)
dict_to_table(dict vals, **kwargs)
dump_sub_regs(node)
stats(obj, sel_links, seconds, show_udp, show_buf)
cli(obj, connection_file, device)
tx_path(obj, phy_reset)
fakesrc_config(obj, link, n_src, src, data_len, rate_rdx)
udp_config(obj, src_id, dst_id, link)
mux_config(obj, detid, crate, slot, link)
read_and_print(hw, reg_list)
validate_device(ctx, param, value)
read_regs(hw, reg_list)
reset(obj, nuke)
zcu_src_config(obj, link, en_n_src, dlen, rate_rdx)