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
190 @property
191 def hermes(self):
192 if self._controller is None:
193 hw = self.cm.getDevice(self.device_id)
194
195 # Identify board
196 is_zcu = hw.getNodes('tx.info')
197 is_detector = hw.getNodes('info')
198
199 if is_zcu:
200 print("zcu mode")
201 tx_mux = hw.getNode('tx')
202 elif is_detector:
203 print("detector mode")
204 tx_mux = hw.getNode()
205 else:
206 raise ValueError(f"{self.device_id} is neither a zcu nor a detector board")
207
208 self.hw = hw
209 # pprint(vars(self))
210 self._controller = HermesModule(tx_mux)
211
212 return self._controller
213
214 @property
215 def cm(self):
216 if self._cm_cm is None:
217 self._cm_cm = uhal.ConnectionManager(self.connection_file_path)
218
219 return self._cm_cm
220
221
222@click.group(chain=True, context_settings=CONTEXT_SETTINGS)
223@click.option('-c', '--connection-file', default=None, help="IPBus connection file ")
224@click.option('-d', '--device', callback=validate_device, help="IPBus device")
225# @click.pass_context
226@click.pass_obj
227def cli(obj, connection_file, device):
228
229 obj.connection_file_path = connection_file
230 obj.device_id = device
231
232@cli.command()
233@click.pass_obj
234def addrbook(obj):
235
236 t = Table(title="Control hosts")
237 t.add_column('name')
238 # t.add_column('addrtable', style='green')
239 for h in obj.cm.getDevices():
240 t.add_row(h)
241 print(t)
242
243 t = Table(title="Receivers")
244 t.add_column('name')
245 t.add_column('mac', style='green')
246 t.add_column('ip', style='blue')
247 t.add_column('port', style='blue')
248 for h,d in rx_endpoints.items():
249 t.add_row(h, f"0x{d['mac']:012x}", f"{d['ip']}", str(d['port']))
250 print(t)
251
252 t = Table(title="Transmitters")
253 t.add_column('name')
254 t.add_column('mac', style='green')
255 t.add_column('ip', style='blue')
256 t.add_column('port', style='blue')
257 for h,d in tx_endpoints.items():
258 t.add_row(h, f"0x{d['mac']:012x}", f"{d['ip']}", str(d['port']))
259 print(t)
260
261
262@cli.command()
263@click.option('--nuke', is_flag=True, default=None)
264@click.pass_obj
265def reset(obj, nuke):
266
267 hrms = obj.hermes
268
269 if nuke is not None:
270 hrms.get_node('csr.ctrl.nuke').write(0x1)
271 hrms.dispatch()
272
273 time.sleep(0.1)
274
275 hrms.get_node('csr.ctrl.nuke').write(0x0)
276 hrms.dispatch()
277
278 hrms.get_node('csr.ctrl.soft_rst').write(0x1)
279 hrms.dispatch()
280
281 time.sleep(0.1)
282
283 hrms.get_node('csr.ctrl.soft_rst').write(0x0)
284 hrms.dispatch()
285
286
287@cli.command()
288@click.option('--en/--dis', 'enable', default=None)
289@click.option('--buf-en/--buf-dis', 'buf_en', default=None)
290@click.option('--tx-en/--tx-dis', 'tx_en', default=None)
291@click.option('-l', '--link', type=int, default=0)
292@click.pass_obj
293def enable(obj, enable, buf_en, tx_en, link):
294
295 hrms = obj.hermes
296
297 n_mgt = hrms.n_mgt
298
299 if link >= n_mgt:
300 raise ValueError(f"MGT {link} not instantiated")
301
302 hrms.sel_tx_mux(link)
303
304 print()
305
306 tx_en = tx_en if tx_en is not None else enable
307 buf_en = buf_en if buf_en is not None else enable
308
309 if tx_en is not None:
310 print(f"- {'Enabling' if tx_en else 'Disabling'} 'tx block'")
311 hrms.get_node('tx_path.tx_mux.csr.ctrl.tx_en').write(tx_en)
312 print()
313
314 if buf_en is not None:
315 print(f"- {'Enabling' if buf_en else 'Disabling'} 'input buffers'")
316 hrms.get_node('tx_path.tx_mux.csr.ctrl.en_buf').write(buf_en)
317
318 time.sleep(0.1)
319
320 if enable is not None:
321 print(f"- {'Enabling' if enable else 'Disabling'} 'mux'")
322 hrms.get_node('tx_path.tx_mux.csr.ctrl.en').write(enable)
323
324
325
326 hrms.dispatch()
327
328
329 top_ctrl = dump_sub_regs(hrms.get_node('tx_path.tx_mux.csr.ctrl'))
330
331 print(
332 dict_to_hextable(top_ctrl, title=f'Link {link} ctrls', show_header=False),
333 )
334
335
336@cli.command("mux-config")
337@click.argument('detid', type=int)
338@click.argument('crate', type=int)
339@click.argument('slot', type=int)
340@click.option('-l', '--link', type=int, default=0)
341@click.pass_obj
342def mux_config(obj, detid, crate, slot, link):
343 """Configure the UDP blocks """
344
345 hrms = obj.hermes
346
347 hrms.sel_tx_mux(link)
348
349 hrms.get_node('tx_path.tx_mux.mux.ctrl.detid').write(detid)
350 hrms.get_node('tx_path.tx_mux.mux.ctrl.crate').write(crate)
351 hrms.get_node('tx_path.tx_mux.mux.ctrl.slot').write(slot)
352
353 mux_ctrl = dump_sub_regs(hrms.get_node('tx_path.tx_mux.mux.ctrl'))
354
355 print(
356 dict_to_hextable(mux_ctrl, title=f'Link {link} mux cfg', show_header=False),
357 )
358
359
360@cli.command("udp-config")
361@click.argument('src_id', type=click.Choice(tx_endpoints.keys()))
362@click.argument('dst_id', type=click.Choice(rx_endpoints.keys()))
363@click.option('-l', '--link', type=int, default=0)
364@click.pass_obj
365def udp_config(obj, src_id, dst_id, link):
366 """Configure the UDP blocks """
367
368 filter_control = 0x07400307
369 hrms = obj.hermes
370
371 if link >= hrms.n_mgt:
372 raise ValueError(f"Link {link} not instantiated")
373
374 hrms.sel_udp_core(link)
375
376 dst = rx_endpoints[dst_id]
377 src = tx_endpoints[src_id]
378
379 udp_core_ctrl = f'tx_path.udp_core.udp_core_control'
380
381 hrms.get_node(f'{udp_core_ctrl}.ctrl.filter_control').write(filter_control)
382
383 hrms.get_node(f'{udp_core_ctrl}.src_addr_ctrl.use_external').write(0);
384
385 # Our IP address = 10.73.139.23
386 # print(f"Our ip address: {socket.inet_ntoa(src['ip'].to_bytes(4, 'big'))}")
387 src_u32 = int.from_bytes(socket.inet_aton(src['ip']),"big")
388 print(f"Our ip address: {src['ip']} (0x{src_u32:08x})")
389
390 hrms.get_node(f'{udp_core_ctrl}.src_addr_ctrl.src_ip_addr').write(src_u32)
391
392 # Their IP address = 10.73.139.23
393 # print(f"Their ip address: {socket.inet_ntoa(dst['ip'].to_bytes(4, 'big'))}")
394 dst_u32 = int.from_bytes(socket.inet_aton(dst['ip']),"big")
395 print(f"Their ip address: {dst['ip']} (0x{dst_u32:08x})")
396 hrms.get_node(f'{udp_core_ctrl}.ctrl.dst_ip_addr').write(dst_u32)
397
398 # Our MAC address
399 # Dest MAC address
400 print(f"Our mac address: 0x{src['mac']:012x}")
401 hrms.get_node(f'{udp_core_ctrl}.src_addr_ctrl.src_mac_addr_lower').write(src['mac'] & 0xffffffff)
402 hrms.get_node(f'{udp_core_ctrl}.src_addr_ctrl.src_mac_addr_upper').write((src['mac'] >> 32) & 0xffff)
403
404 # Dest MAC address
405 print(f"Their mac address: 0x{dst['mac']:012x}")
406 hrms.get_node(f'{udp_core_ctrl}.ctrl.dst_mac_addr_lower').write(dst['mac'] & 0xffffffff)
407 hrms.get_node(f'{udp_core_ctrl}.ctrl.dst_mac_addr_upper').write((dst['mac'] >> 32) & 0xffff)
408
409 # Ports
410 hrms.get_node(f'{udp_core_ctrl}.src_addr_ctrl.src_port').write(src['port'])
411 hrms.get_node(f'{udp_core_ctrl}.ctrl.dst_port').write(dst['port'])
412
413 hrms.dispatch()
414
415
416@cli.command("zcu-src-config")
417@click.option('-l', '--link', type=int, default=0)
418@click.option('-n', '--en-n-src', type=click.IntRange(0, MAX_SRCS_P_MGT), default=1)
419@click.option('-d', '--dlen', type=click.IntRange(0, 0xfff), default=0x383)
420@click.option('-r', '--rate-rdx', type=click.IntRange(0, 0x3f), default=0xa)
421@click.pass_obj
422def zcu_src_config(obj, link, en_n_src, dlen, rate_rdx):
423 """Configure trivial data sources"""
424
425 hw = obj.cm.getDevice(obj.device_id)
426 #hw = obj.hw
427 hrms = obj.hermes
428
429 # n_mgt = obj.n_mgt
430 # n_src = obj.n_src
431 # n_srcs_p_mgt = n_src//n_mgt
432
433 #print(hw)
434 #pprint(vars(obj))
435 #pprint(vars(hrms))
436 #print(hw)
437
438 if link >= hrms.n_mgt:
439 raise ValueError(f"MGT {link} not instantiated")
440
441 if en_n_src > hrms.n_srcs_p_mgt:
442 raise ValueError(f"{en_n_src} must be lower than the number of generators per link ({hrms.n_srcs_p_mgt})")
443
444 for i in range(hrms.n_srcs_p_mgt):
445 src_id = hrms.n_srcs_p_mgt*link+i
446 hw.getNode('src.csr.ctrl.sel').write(src_id)
447 src_en = (i<en_n_src)
448 print(f'Configuring generator {src_id} : {src_en}')
449 #data_gen = hw.getNode("src.data_src.src.params.write")
450 #data_gen.getNode("gap").write(1)
451 #hw.getNode('src.data_src.src.csr.ctrl.start_stop').write(src_en)
452 #if not src_en:
453 # continue
454
458 hw.dispatch()
459
460
461 regs = {}
462 for i in range(hrms.n_srcs_p_mgt):
463 hw.getNode('src.csr.ctrl.sel').write(i)
464 src_regs = dump_sub_regs(hw.getNode('src.csr'))
465 data_src_regs = dump_sub_regs(hw.getNode('src.data_src'))
466
467 grid = Table.grid()
468 grid.add_column("ctrl")
469 grid.add_column("stat")
470 grid.add_row(
471 dict_to_hextable(src_regs, title='src_ctrl', show_header=False),
472 #dict_to_hextable(data_src_regs, title='data_src_ctrl', show_header=False)
473 )
474 for j in range(hrms.n_srcs_p_mgt):
475 grid.add_row(
476 dict_to_hextable(data_src_regs, title='data_src_ctrl', show_header=False)
477
478 )
479 print(f'Link {i}')
480 print(grid)
481
482 #regs[i] = dump_sub_regs(hw.getNode('src.csr.stat.no_of_src_blocks'))
483
484 # Create the summary table
485 t = Table()
486
487
488
489 # Add 1 column for the reg name, and as many as the number of sources
490 #t.add_column('name')
491 # for j in range(hrms.n_srcs_p_mgt):
492 # t.add_column(f'Src {j}', style='green')
493
494 # for n in hw.getNode('src').getNodes():
495 # t.add_row(n, *[hex(regs[i][n]) for i in range(hrms.n_srcs_p_mgt)])
496
497
498 #print(t)
499
500@cli.command("fakesrc-config")
501@click.option('-l', '--link', type=int, default=0)
502@click.option('-n', '--n-src', type=click.IntRange(0, MAX_SRCS_P_MGT), default=1)
503@click.option('-s', '--src', type=click.IntRange(0, MAX_SRCS_P_MGT), default=None, multiple=True)
504@click.option('-k', '--data-len', type=click.IntRange(0, 0xfff), default=0x383)
505@click.option('-r', '--rate-rdx', type=click.IntRange(0, 0x3f), default=0xa)
506@click.pass_obj
507def fakesrc_config(obj, link, n_src, src, data_len, rate_rdx):
508 """Configure trivial data sources"""
509
510 hrms = obj.hermes
511
512 all_srcs = set(range(hrms.n_srcs_p_mgt))
513 en_srcs = set(src)
514
515 # print(all_srcs)
516 # print(en_srcs)
517
518 if en_srcs != set.intersection(all_srcs, en_srcs):
519 raise ValueError("AAARGH")
520
521 # print(src)
522 # return
523
524 hrms.sel_tx_mux(link)
525
526 if n_src > hrms.n_srcs_p_mgt:
527 raise ValueError(f"{n_src} must be lower than the number of generators per link ({n_srcs_p_mgt})")
528
529 was_en = hrms.get_node('tx_path.tx_mux.csr.ctrl.en_buf').read()
530 # disable buffer before reconfiguring "or bad things will happen"
531 hrms.get_node('tx_path.tx_mux.csr.ctrl.en_buf').write(0x0)
532 hrms.dispatch()
533
534 for src_id in range(hrms.n_srcs_p_mgt):
535 hrms.sel_tx_mux_buf(src_id)
536
537 src_en = (src_id in en_srcs)
538 print(f'Configuring generator {src_id} : {src_en}')
539 # hw.write(f'tx.buf.ctrl.fake_en', src_en)
540 hrms.get_node('tx_path.tx_mux.buf.ctrl.fake_en').write(src_en)
541 if not src_en:
542 continue
543
544 hrms.get_node('tx_path.tx_mux.buf.ctrl.dlen').write(data_len)
545
546 hrms.get_node('tx_path.tx_mux.buf.ctrl.rate_rdx').write(rate_rdx)
547 hrms.dispatch()
548
549 hrms.get_node('tx_path.tx_mux.csr.ctrl.en_buf').write(was_en.value())
550 hrms.dispatch()
551
552
553@cli.command()
554@click.pass_obj
555@click.option('-l', '--links', 'sel_links', type=click.Choice(mgts_all), multiple=True, default=None)
556@click.option('-s', '--seconds', type=int, default=0)
557@click.option('-u/-U', '--show-udp/--hide-udp', 'show_udp', default=True)
558@click.option('-b/-B', '--show-buf/--hide-buf', 'show_buf', default=True)
559
560def stats(obj, sel_links, seconds, show_udp, show_buf):
561 """Simple program that greets NAME for a total of COUNT times."""
562
563 hrms = obj.hermes
564
565 mgts = list(range(hrms.n_mgt))
566
567 # deal with defaults
568 if not sel_links:
569 sel_links = mgts
570 else:
571 sel_links = [int(s) for s in sel_links]
572
573
574 # Check for existance
575 if not set(sel_links).issubset(mgts):
576 print(sel_links, mgts)
577 raise ValueError(f"MGTs {set(sel_links)-set(mgts)} are not instantiated")
578
579 # mgts = [int(s) for s in (mgts if mgts else [0])]
580
581 print(f"Sampling hermes counters for {seconds}s")
582 hrms.sample_ctrs(seconds)
583
584 ts_l = hrms.get_node('samp.samp_ts_l').read()
585 ts_h = hrms.get_node('samp.samp_ts_h').read()
586 hrms.dispatch()
587
588 print(f"Current timestamp : {(ts_h.value() << 32) + ts_l.value()}")
589
590 info_data = dump_sub_regs(hrms.get_node('info'))
591 print(dict_to_hextable(info_data, title='hermes info', show_header=False))
592
593 # n_srcs_p_mgt = n_src//n_mgt
594
595 for i in sel_links:
596 print(f'---Tx Mux {i} Status---')
597
598 hrms.sel_tx_mux(i)
599
600
601 # cli control registers
602 top_ctrl = dump_sub_regs(hrms.get_node('tx_path.tx_mux.csr.ctrl'))
603 top_stat = dump_sub_regs(hrms.get_node('tx_path.tx_mux.csr.stat'))
604 ctrl_mux = dump_sub_regs(hrms.get_node('tx_path.tx_mux.mux.ctrl'))
605 stat_mux = dump_sub_regs(hrms.get_node('tx_path.tx_mux.mux.stat'))
606
607 grid = Table.grid()
608 grid.add_column("ctrl")
609 grid.add_column("stat")
610 grid.add_row(
611 dict_to_hextable(top_ctrl, title='tx_mux ctrl', show_header=False),
612 dict_to_hextable(top_stat, title='tx mux stat', show_header=False),
613 dict_to_hextable(ctrl_mux, title="mux ctrl", show_header=False),
614 dict_to_hextable(stat_mux, title="mux stat", show_header=False),
615 )
616 print(grid)
617
618
619 if show_udp:
620 hrms.sel_udp_core(i)
621 ctrl_udp_src = dump_sub_regs(hrms.get_node(f'tx_path.udp_core.udp_core_control.src_addr_ctrl'))
622 ctrl_udp = dump_sub_regs(hrms.get_node(f'tx_path.udp_core.udp_core_control.ctrl'))
623 ctrl_flt_udp = dump_sub_regs(hrms.get_node(f'tx_path.udp_core.udp_core_control.ctrl.filter_control'))
624 if hrms.get_nodes(f'tx_path.udp_core.udp_core_control.rx_packet_counters'):
625 print("New tx counters found")
626 stat_rx_udp = dump_sub_regs(hrms.get_node(f'tx_path.udp_core.udp_core_control.rx_packet_counters'))
627 stat_tx_udp = dump_sub_regs(hrms.get_node(f'tx_path.udp_core.udp_core_control.tx_packet_counters'))
628 else:
629 print("No new tx counters found")
630 stat_rx_udp = dump_sub_regs(hrms.get_node(f'tx_path.udp_core.udp_core_control.tx_packet_counters'))
631 stat_tx_udp = {'-':0}
632
633
634 ctrl_srcdst = {}
635 ctrl_srcdst['src_ip'] = ctrl_udp_src['src_ip_addr']
636 ctrl_srcdst['dst_ip'] = ctrl_udp['dst_ip_addr']
637 ctrl_srcdst['src_mac'] = (ctrl_udp_src['src_mac_addr_upper'] << 32) + ctrl_udp_src['src_mac_addr_lower']
638 ctrl_srcdst['dst_mac'] = (ctrl_udp['dst_mac_addr_upper'] << 32) + ctrl_udp['dst_mac_addr_lower']
639 ctrl_srcdst['src_port'] = ctrl_udp_src['src_port']
640 ctrl_srcdst['dst_port'] = ctrl_udp['dst_port']
641
642 grid = Table.grid()
643 grid.add_column("ctrl")
644 grid.add_column("stat")
645 grid.add_row(
646 # dict_to_hextable(ctrl_udp, title="udp ctrl", show_header=False),
647 dict_to_hextable(ctrl_srcdst, title="udp src/dst", show_header=False),
648 dict_to_hextable(ctrl_flt_udp, title="udp filter", show_header=False),
649 dict_to_hextable(stat_rx_udp, title="udp rx stat", show_header=False),
650 dict_to_hextable(stat_tx_udp, title="udp tx stat", show_header=False),
651 )
652 print(grid)
653
654 if show_buf:
655 ibuf_stats = {}
656
657 src_ids = tuple(range(hrms.n_srcs_p_mgt))
658 for j in src_ids:
659 # hw.write('tx.mux.csr.ctrl.sel_buf',j)
660 hrms.sel_tx_mux_buf(j)
661 s = dump_sub_regs(hrms.get_node('tx_path.tx_mux.buf'))
662 s['blk_acc'] = (s['blk_acc_h']<<32)+s['blk_acc_l']
663 s['blk_oflow'] = (s['blk_oflow_h']<<32)+s['blk_oflow_l']
664 s['blk_rej'] = (s['blk_rej_h']<<32)+s['blk_rej_l']
665 s['ts'] = (s['ts_h']<<32)+s['ts_l']
666 s['vol'] = (s['vol_h']<<32)+s['vol_l']
667 s['blk_longlast'] = (s['blk_longlast_h']<<32)+s['blk_longlast_l']
668 s['blk_lastnotval'] = (s['blk_lastnotval_h']<<32)+s['blk_lastnotval_l']
669
670 for k in tuple(s.keys()):
671 for n in ('blk_acc_', 'blk_oflow_', 'blk_rej_', 'ts_', 'vol_', 'blk_longlast_', 'blk_lastnotval_'):
672 if k.startswith(n):
673 del s[k]
674
675 for k in ('ctrl', 'stat', 'buf_mon'):
676 del s[k]
677
678
679 ibuf_stats[j] = s
680
681 # Create the summary table
682 t = Table()
683
684 # Add 1 column for the reg name, and as many as the number of sources
685 t.add_column('name')
686 for j in src_ids:
687 t.add_column(f'Buf {j}', style='green')
688
689 # Unify the reg list (useless?)
690 reg_names = set()
691 for k,v in ibuf_stats.items():
692 reg_names = reg_names.union(v.keys())
693
694 for n in sorted(reg_names):
695 t.add_row(n,*(hex(ibuf_stats[j][n]) for j in src_ids))
696 print(t)
697
698
699@cli.command()
700@click.pass_obj
701@click.option('-r', '--phy-reset', is_flag=True, default=None, help="Reset the phy block")
702def tx_path(obj, phy_reset):
703 '''
704 TX path low-level controls
705 '''
706
707 hrms = obj.hermes
708 pcs_pma_node = hrms.get_node('pcs_pma')
709
710 if phy_reset:
711 print("[cyan]Resetting phy[/cyan]")
712 pcs_pma_node.getNode('debug.csr.ctrl.phy_reset').write(0x1)
713 pcs_pma_node.getNode('debug.csr.ctrl.phy_reset').write(0x0)
714 pcs_pma_node.getClient().dispatch()
715 print("[cyan]Phy reset completed[/cyan]")
716
717
718@cli.command()
719@click.pass_obj
721 '''
722 TX path clock monitoring
723 '''
724 clk_ids = [
725 'ref_clk_out',
726 'tx_mii_clk_0',
727 'rx_clk_out_0',
728 ]
729
730 hrms = obj.hermes
731 pcs_pma_node = hrms.get_node('pcs_pma')
732
733 # print(pcs_pma_node.getNode('debug.csr.stat').getNodes())
734 stats = { n: pcs_pma_node.getNode('debug.csr.stat.'+n).read() for n in pcs_pma_node.getNode('debug.csr.stat').getNodes() }
735 pcs_pma_node.getClient().dispatch()
736
737 print(dict_to_hextable(stats, title='TX clock registers', show_header=False))
738
739
740 clk_freqs = {}
741 for idx, clk_id in enumerate(clk_ids):
742
743 fn = pcs_pma_node.getNode('freq')
744 fn.getNode('ctrl.chan_sel').write(idx)
745 fn.getNode('ctrl.en_crap_mode').write(0x0)
746 fn.getClient().dispatch()
747
748 time.sleep(1.1)
749 c = fn.getNode('freq.count').read()
750 v = fn.getNode('freq.valid').read()
751 fn.getClient().dispatch()
752
753 if v == 0:
754 print(f'[red]Failed to measure frequency of clock {clk_id}[/red]')
755
756 ipb_freq = 100000000
757 # ipb_freq = 125000000
758 denominator = 2**24
759 true_count = int(c)*64
760
761 clk_freq = true_count/denominator*ipb_freq
762 # print(f"Measured {clk_id} clock freq {clk_freq/1000000:04} MHz")
763 print(f"Measured clock '{clk_id}'")
764 clk_freqs[clk_id] = clk_freq
765
766
767 print(dict_to_table({k:f"{v/1000000:7.3f} MHz" for k,v in clk_freqs.items()}, title='TX clocks', show_header=False))
768
769
770
771
772
773if __name__ == '__main__':
774 FORMAT = "%(message)s"
775 logging.basicConfig(
776 level="INFO", format=FORMAT, datefmt="[%X]", handlers=[RichHandler()]
777 )
778 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)