10from rich.table
import Table
11from rich.logging
import RichHandler
16from pprint
import pprint
27 t.add_column(
'value', style=
'green')
28 for k,v
in vals.items():
36 t.add_column(
'value', style=
'green')
37 for k,v
in vals.items():
59 for i
in sorted(node.getNodes()):
60 regs[i] = node.getNode(i).read()
61 node.getClient().dispatch()
63 return {k: v.value()
for k, v
in regs.items()}
69 node.getClient().dispatch()
70 return {node.getId(): v.value()}
81 magic = self.
node.getNode(
'info.magic').read()
84 if self.
magic != 0xdeadbeef:
85 raise ValueError(f
"Magic number check failed. Expected '0xdeadbeef', found '{hex(self.magic)}'")
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()
102 return self.
node.getNode(id)
105 return self.
node.getNodes(regex)
113 self.
node.getNode(
'samp.ctrl.samp').write(
True)
114 self.
node.getNode(
'samp.ctrl.samp').write(
False)
117 self.
node.getNode(
'samp.ctrl.samp').write(
True)
120 self.
node.getNode(
'samp.ctrl.samp').write(
False)
128 raise ValueError(f
"Link {i} does not exist ({self.n_mgt})")
130 self.
get_node(
'tx_path.csr_tx_mux.ctrl.tx_mux_sel').write(i)
132 j = self.
get_node(
'tx_path.csr_tx_mux.ctrl.tx_mux_sel').read()
134 print(f
"Link {j} selected")
140 raise ValueError(f
"Input buffer {i} does not exist ({self.n_src})")
142 self.
node.getNode(
'tx_path.tx_mux.csr.ctrl.sel_buf').write(i)
148 raise ValueError(f
"Link {i} does not exist ({self.n_mgt})")
150 self.
node.getNode(
'tx_path.csr_udp_core.ctrl.udp_core_sel').write(i)
159mgts_all = tuple(str(i)
for i
in range(MAX_MGT))
161CONTEXT_SETTINGS = dict(help_option_names=[
'-h',
'--help'])
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])
183 uhal.setLogLevelTo(uhal.LogLevel.WARNING)
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".')
200 is_zcu = hw.getNodes(
'tx.info')
201 is_detector = hw.getNodes(
'info')
205 tx_mux = hw.getNode(
'tx')
207 print(
"detector mode")
208 tx_mux = hw.getNode()
210 raise ValueError(f
"{self.device_id} is neither a zcu nor a detector board")
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")
231def cli(obj, connection_file, device):
232 obj.connection_file_path = connection_file
233 obj.device_id = device
239 t = Table(title=
"Control hosts")
242 for h
in obj.cm.getDevices():
246 t = Table(title=
"Receivers")
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']))
255 t = Table(title=
"Transmitters")
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']))
266@click.option('--nuke', is_flag=True, default=None)
269 obj.check_device_id()
273 hrms.get_node(
'csr.ctrl.nuke').write(0x1)
278 hrms.get_node(
'csr.ctrl.nuke').write(0x0)
281 hrms.get_node(
'csr.ctrl.soft_rst').write(0x1)
286 hrms.get_node(
'csr.ctrl.soft_rst').write(0x0)
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)
296def enable(obj, enable, buf_en, tx_en, link):
298 obj.check_device_id()
304 raise ValueError(f
"MGT {link} not instantiated")
306 hrms.sel_tx_mux(link)
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
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)
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)
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)
333 top_ctrl =
dump_sub_regs(hrms.get_node(
'tx_path.tx_mux.csr.ctrl'))
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)
347 """Configure the UDP blocks """
349 obj.check_device_id()
352 hrms.sel_tx_mux(link)
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)
358 mux_ctrl =
dump_sub_regs(hrms.get_node(
'tx_path.tx_mux.mux.ctrl'))
361 dict_to_hextable(mux_ctrl, title=f
'Link {link} mux cfg', show_header=
False),
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)
371 """Configure the UDP blocks """
373 obj.check_device_id()
374 filter_control = 0x07400307
377 if link >= hrms.n_mgt:
378 raise ValueError(f
"Link {link} not instantiated")
380 hrms.sel_udp_core(link)
382 dst = rx_endpoints[dst_id]
383 src = tx_endpoints[src_id]
385 udp_core_ctrl = f
'tx_path.udp_core.udp_core_control'
387 hrms.get_node(f
'{udp_core_ctrl}.ctrl.filter_control').write(filter_control)
389 hrms.get_node(f
'{udp_core_ctrl}.src_addr_ctrl.use_external').write(0);
393 src_u32 = int.from_bytes(socket.inet_aton(src[
'ip']),
"big")
394 print(f
"Our ip address: {src['ip']} (0x{src_u32:08x})")
396 hrms.get_node(f
'{udp_core_ctrl}.src_addr_ctrl.src_ip_addr').write(src_u32)
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)
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)
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)
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'])
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)
429 """Configure trivial data sources"""
431 obj.check_device_id()
432 hw = obj.cm.getDevice(obj.device_id)
445 if link >= hrms.n_mgt:
446 raise ValueError(f
"MGT {link} not instantiated")
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})")
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}')
469 for i
in range(hrms.n_srcs_p_mgt):
470 hw.getNode(
'src.csr.ctrl.sel').write(i)
475 grid.add_column(
"ctrl")
476 grid.add_column(
"stat")
481 for j
in range(hrms.n_srcs_p_mgt):
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)
515 """Configure trivial data sources"""
517 obj.check_device_id()
520 all_srcs = set(range(hrms.n_srcs_p_mgt))
526 if en_srcs != set.intersection(all_srcs, en_srcs):
527 raise ValueError(
"AAARGH")
532 hrms.sel_tx_mux(link)
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})")
537 was_en = hrms.get_node(
'tx_path.tx_mux.csr.ctrl.en_buf').read()
539 hrms.get_node(
'tx_path.tx_mux.csr.ctrl.en_buf').write(0x0)
542 for src_id
in range(hrms.n_srcs_p_mgt):
543 hrms.sel_tx_mux_buf(src_id)
545 src_en = (src_id
in en_srcs)
546 print(f
'Configuring generator {src_id} : {src_en}')
548 hrms.get_node(
'tx_path.tx_mux.buf.ctrl.fake_en').write(src_en)
552 hrms.get_node(
'tx_path.tx_mux.buf.ctrl.dlen').write(data_len)
554 hrms.get_node(
'tx_path.tx_mux.buf.ctrl.rate_rdx').write(rate_rdx)
557 hrms.get_node(
'tx_path.tx_mux.csr.ctrl.en_buf').write(was_en.value())
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)
568def stats(obj, sel_links, seconds, show_udp, show_buf):
569 """Simple program that greets NAME for a total of COUNT times."""
571 obj.check_device_id()
574 mgts = list(range(hrms.n_mgt))
580 sel_links = [int(s)
for s
in sel_links]
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")
590 print(f
"Sampling hermes counters for {seconds}s")
591 hrms.sample_ctrs(seconds)
593 ts_l = hrms.get_node(
'samp.samp_ts_l').read()
594 ts_h = hrms.get_node(
'samp.samp_ts_h').read()
597 print(f
"Current timestamp : {(ts_h.value() << 32) + ts_l.value()}")
605 print(f
'---Tx Mux {i} Status---')
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'))
617 grid.add_column(
"ctrl")
618 grid.add_column(
"stat")
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'))
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}
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']
652 grid.add_column(
"ctrl")
653 grid.add_column(
"stat")
666 src_ids = tuple(range(hrms.n_srcs_p_mgt))
669 hrms.sel_tx_mux_buf(j)
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']
679 for k
in tuple(s.keys()):
680 for n
in (
'blk_acc_',
'blk_oflow_',
'blk_rej_',
'ts_',
'vol_',
'blk_longlast_',
'blk_lastnotval_'):
684 for k
in (
'ctrl',
'stat',
'buf_mon'):
696 t.add_column(f
'Buf {j}', style=
'green')
700 for k,v
in ibuf_stats.items():
701 reg_names = reg_names.union(v.keys())
703 for n
in sorted(reg_names):
704 t.add_row(n,*(hex(ibuf_stats[j][n])
for j
in src_ids))
710@click.option('-r', '--phy-reset', is_flag=True, default=None, help="Reset the phy block")
713 TX path low-level controls
716 obj.check_device_id()
718 pcs_pma_node = hrms.get_node(
'pcs_pma')
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]")
732 TX path clock monitoring
740 obj.check_device_id()
742 pcs_pma_node = hrms.get_node(
'pcs_pma')
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()
748 print(
dict_to_hextable(stats, title=
'TX clock registers', show_header=
False))
752 for idx, clk_id
in enumerate(clk_ids):
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()
760 c = fn.getNode(
'freq.count').read()
761 v = fn.getNode(
'freq.valid').read()
762 fn.getClient().dispatch()
765 print(f
'[red]Failed to measure frequency of clock {clk_id}[/red]')
770 true_count = int(c)*64
772 clk_freq = true_count/denominator*ipb_freq
774 print(f
"Measured clock '{clk_id}'")
775 clk_freqs[clk_id] = clk_freq
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))
784if __name__ ==
'__main__':
785 FORMAT =
"%(message)s"
787 level=
"INFO", format=FORMAT, datefmt=
"[%X]", handlers=[RichHandler()]
sample_ctrs(self, int seconds)
sel_udp_core(self, int i)
sel_tx_mux_buf(self, int i)
dict_to_hextable(dict vals, **kwargs)
dict_to_table(dict vals, **kwargs)
stats(obj, sel_links, seconds, show_udp, show_buf)
cli(obj, connection_file, device)
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)
zcu_src_config(obj, link, en_n_src, dlen, rate_rdx)