Skip to content

Commit 288ef70

Browse files
committed
mem_logger [FEATURE]: Load statistics using new logger_stats package
1 parent ad79f3a commit 288ef70

File tree

5 files changed

+333
-279
lines changed

5 files changed

+333
-279
lines changed
Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +0,0 @@
1-
from mem_logger import mem_logger
2-
3-
__all__ = ["mem_logger"]
Lines changed: 157 additions & 185 deletions
Original file line numberDiff line numberDiff line change
@@ -1,207 +1,165 @@
11
#!/usr/bin/env python3
22
# Copyright (C) 2022 CESNET z. s. p. o.
33
# Author(s): Lukas Nevrkla <[email protected]>
4+
#
5+
# Package for loading statistics from mem_logger component
46

5-
import json
67
import argparse
8+
import numpy as np
79
import nfb
810
from data_logger.data_logger import DataLogger
11+
import logger_stats.logger_stats as Stats
912

1013

1114
class MemLogger(DataLogger):
1215

1316
DT_COMPATIBLE = "netcope,mem_logger"
1417

15-
_BIT_LATENCY_TO_FIRST = 0
16-
1718
def __init__(self, **kwargs):
1819
try:
1920
super().__init__(**kwargs)
21+
except Exception as e:
22+
raise Exception(f"ERROR while opening MemLogger component:\n {e}")
2023

21-
ctrli = self.load_ctrl(False)
22-
self.config["MEM_DATA_WIDTH"] = self.get_bits(ctrli, self.mi_width, self.mi_width * 0)
23-
self.config["MEM_ADDR_WIDTH"] = self.get_bits(ctrli, self.mi_width, self.mi_width * 1)
24-
self.config["MEM_BURST_WIDTH"] = self.get_bits(ctrli, self.mi_width, self.mi_width * 2)
25-
self.config["MEM_FREQ_KHZ"] = self.get_bits(ctrli, self.mi_width, self.mi_width * 3)
26-
27-
except Exception:
28-
print("ERROR while opening MemLogger component!\nMaybe unsupported FPGA firmware?!")
29-
exit(1)
24+
self.stats = self.init_stats()
3025

3126
def set_config(self, latency_to_first):
3227
self.set_ctrlo(latency_to_first & 1)
3328

34-
def ticks_to_s(self, ticks):
35-
return ticks / (self.config["MEM_FREQ_KHZ"] * 1000.0)
36-
37-
def ticks_to_flow(self, words, ticks):
38-
s = self.ticks_to_s(ticks)
39-
if s == 0:
40-
return 0
41-
return (words * self.config["MEM_DATA_WIDTH"]) / s
42-
43-
# Remove leading and trailing zeros
44-
def trim_hist(self, data):
45-
res = {}
46-
res_tmp = {}
47-
48-
started = False
49-
for k, v in data.items():
50-
if v != 0 or started:
51-
res_tmp[k] = v
52-
started = True
53-
elif v == 0 or not started:
54-
res_tmp = {k: v}
55-
if v != 0:
56-
res = res_tmp.copy()
57-
58-
return res
59-
60-
def latency_hist_step(self):
61-
hist_max = 2 ** self.config["VALUE_WIDTH"][0]
62-
hist_step = hist_max / self.config["HIST_BOX_CNT"][0]
63-
return self.ticks_to_s(hist_step - 1) * 10**9
64-
65-
def load_stats(self):
66-
stats = {}
67-
68-
ctrlo = self.load_ctrl(True)
69-
stats["latency_to_first"] = (ctrlo >> self._BIT_LATENCY_TO_FIRST) & 1
70-
71-
# Cnters
72-
stats["wait"] = self.load_cnter(0)
73-
stats["request_hold"] = self.load_cnter(1)
74-
stats["rdy_hold_write"] = self.load_cnter(2)
75-
stats["rdy_hold_read"] = self.load_cnter(3)
76-
77-
stats["wr_ticks"] = self.load_cnter(4)
78-
stats["rd_ticks"] = self.load_cnter(5)
79-
stats["total_ticks"] = self.load_cnter(6)
80-
stats["wr_req_cnt"] = self.load_cnter(7)
81-
stats["wr_req_words"] = self.load_cnter(8)
82-
stats["rd_req_cnt"] = self.load_cnter(9)
83-
stats["rd_req_words"] = self.load_cnter(10)
84-
stats["rd_resp_words"] = self.load_cnter(11)
85-
stats["err_zero_burst"] = self.load_cnter(12)
86-
stats["err_simult_rw"] = self.load_cnter(13)
87-
88-
# Values
89-
stats["latency"] = self.load_value(0)
29+
def init_stats(self):
30+
stats = Stats.LoggerStats('Mem logger', logger=self)
31+
32+
# Constants #
33+
34+
constants = [
35+
"MEM_DATA_WIDTH",
36+
"MEM_ADDR_WIDTH",
37+
"MEM_BURST_WIDTH",
38+
"MEM_FREQ_KHZ",
39+
]
40+
stats.add_stats(
41+
name='Constants',
42+
names=constants,
43+
indexes=list(range(len(constants))),
44+
constructor=lambda i, n: Stats.Constant(i, n)
45+
)
46+
stats.load()
47+
48+
freq = stats['Constants']['MEM_FREQ_KHZ'] * 1000.0
49+
word_b = stats['Constants']['MEM_DATA_WIDTH']
50+
51+
# Counters #
52+
53+
counters = [
54+
"no req + not rdy",
55+
"no req + rdy",
56+
"wr + not rdy",
57+
"rd + not rdy",
58+
"wr ticks",
59+
"rd ticks",
60+
"total ticks",
61+
"wr req cnt",
62+
"wr req words",
63+
"rd req cnt",
64+
"rd req words",
65+
"rd resp words",
66+
"err zero burst",
67+
"err simult rw",
68+
]
69+
70+
def counter_i(name):
71+
return counters.index(name)
72+
73+
# Requests #
74+
75+
reqs = ['wr req cnt', 'wr req words', 'rd req cnt', 'rd req words', 'rd resp words']
76+
stats.add_stats(
77+
name='Requests',
78+
names=reqs,
79+
indexes=list(map(counter_i, reqs)),
80+
constructor=lambda i, n: Stats.Counter(i, n)
81+
)
82+
83+
# Data flow #
84+
85+
stats_flow = Stats.LoggerStats('Data flow')
86+
stats.add_stat(stats_flow)
87+
88+
stats_flow.add_stat(Stats.FlowCounter(
89+
index_words=counter_i('wr req words'), index_ticks=counter_i('wr ticks'),
90+
freq=freq, word_bits=word_b, name='wr flow'
91+
))
92+
stats_flow.add_stat(Stats.FlowCounter(
93+
index_words=counter_i('rd resp words'), index_ticks=counter_i('rd ticks'),
94+
freq=freq, word_bits=word_b, name='rd flow'
95+
))
96+
97+
# Values #
98+
99+
stats_val = Stats.LoggerStats('Values')
100+
stats.add_stat(stats_val)
101+
102+
stats_val.add_stat(Stats.Value(
103+
index=0, name='latency',
104+
convert=Stats.ConvertTime(freq, units='ns'),
105+
format=Stats.FormatDefaultValue(units='ns', format=Stats.FormatDefault(decimal=1))
106+
))
90107
if self.config["VALUE_CNT"] > 1:
91-
stats["paralel_read"] = self.load_value(1)
92-
93-
# Calculate time and flow
94-
stats["wr_time_ms"] = self.ticks_to_s(stats['wr_ticks']) * 10**3
95-
stats["wr_flow_gbs"] = self.ticks_to_flow(stats['wr_req_words'], stats['wr_ticks']) / 10**9
96-
97-
stats["rd_time_ms"] = self.ticks_to_s(stats['rd_ticks']) * 10**3
98-
stats["rd_flow_gbs"] = self.ticks_to_flow(stats['rd_resp_words'], stats['rd_ticks']) / 10**9
99-
100-
stats["total_time_ms"] = self.ticks_to_s(stats['total_ticks']) * 10**3
101-
stats["total_flow_gbs"] = self.ticks_to_flow(stats['wr_req_words'] + stats['rd_resp_words'], stats['total_ticks']) / 10**9
102-
103-
# Calculate latency
104-
stats["latency"]["min_ns"] = self.ticks_to_s(stats["latency"]["min"]) * 10**9
105-
stats["latency"]["max_ns"] = self.ticks_to_s(stats["latency"]["max"]) * 10**9
106-
stats["latency"]["avg_ns"] = self.ticks_to_s(stats["latency"]["avg"]) * 10**9
107-
108-
# Calculate latency histogram
109-
hist_step = self.config["HIST_STEP"][0]
110-
stats["latency"]["hist_ns"] = {}
108+
stats_val.add_stat(Stats.Value(index=1, name='paralel reads'))
109+
110+
# Duration #
111+
112+
stats_dur = Stats.LoggerStats('Test duration')
113+
stats.add_stat(stats_dur)
114+
115+
stats_dur.add_stat(Stats.TimeCounter(
116+
index=counter_i('wr ticks'), freq=freq,
117+
name='wr time', units='ms'
118+
))
119+
stats_dur.add_stat(Stats.TimeCounter(
120+
index=counter_i('rd ticks'), freq=freq,
121+
name='rd time', units='ms'
122+
))
123+
stats_dur.add_stat(Stats.TimeCounter(
124+
index=counter_i('total ticks'), freq=freq,
125+
name='total time', units='ms'
126+
))
127+
128+
# Errors #
129+
130+
errs = ['err zero burst', 'err simult rw']
131+
stats.add_stats(
132+
name='Errors',
133+
names=errs,
134+
indexes=list(map(counter_i, errs)),
135+
constructor=lambda i, n: Stats.Counter(i, n)
136+
)
137+
138+
# Ready signal status #
139+
140+
rdy_status = ["no req + not rdy", "no req + rdy", "wr + not rdy", "rd + not rdy"]
141+
stats.add_stats(
142+
name='Ready signal status',
143+
names=rdy_status,
144+
indexes=list(map(counter_i, rdy_status)),
145+
constructor=lambda i, n: Stats.Counter(i, n)
146+
)
147+
148+
# Special statistics #
149+
150+
BIT_LATENCY_TO_FIRST = 0
151+
ctrlo = self.load_ctrl(True)
152+
latency_to_first = (ctrlo >> BIT_LATENCY_TO_FIRST) & 1
153+
stats.add_stat(Stats.Custom(name='latency to first word', data=latency_to_first))
111154

112-
for i, v in enumerate(stats["latency"]["hist"]):
113-
end = self.ticks_to_s((i + 1) * hist_step - 1) * 10**9
114-
stats["latency"]["hist_ns"][end] = v
115-
stats["latency"]["hist_ns"] = self.trim_hist(stats["latency"]["hist_ns"])
155+
stats.add_calc_stats(self.calc_stats)
116156

117157
return stats
118158

119-
def stats_to_json(self, stats):
120-
res = json.dumps(stats, indent=4)
121-
print(res)
122-
123-
def line_to_str(self, txt, val, unit=""):
124-
if isinstance(val, int):
125-
val = f"{val:<15}"
126-
elif isinstance(val, float):
127-
val = f"{val:< .2f}"
128-
129-
if unit != "":
130-
unit = f"[{unit}]"
131-
return f"{txt:<20} {val} {unit}\n"
132-
133-
def stats_to_str(self, stats):
134-
res = ""
135-
res += "Mem_logger statistics:\n"
136-
res += "----------------------\n"
137-
res += self.line_to_str("write requests ", stats['wr_req_cnt'])
138-
res += self.line_to_str(" write words ", stats['wr_req_words'])
139-
res += self.line_to_str("read requests ", stats['rd_req_cnt'])
140-
res += self.line_to_str(" requested words", stats['rd_req_words'])
141-
res += self.line_to_str(" received words ", stats['rd_resp_words'])
142-
res += "Handshakes:\n"
143-
res += self.line_to_str(" avmm rdy hold ", stats['rdy_hold_read'] + stats['rdy_hold_write'])
144-
res += self.line_to_str(" avmm rdy hold (rd) ", stats['rdy_hold_read'])
145-
res += self.line_to_str(" avmm rdy hold (wr) ", stats['rdy_hold_write'])
146-
res += self.line_to_str(" no request ", stats["request_hold"])
147-
res += self.line_to_str(" wait ", stats["wait"])
148-
res += "Flow:\n"
149-
res += self.line_to_str(" write", stats['wr_flow_gbs'], "Gb/s")
150-
res += self.line_to_str(" read ", stats['rd_flow_gbs'], "Gb/s")
151-
res += self.line_to_str(" total", stats['total_flow_gbs'], "Gb/s")
152-
res += "Time:\n"
153-
res += self.line_to_str(" write", stats['wr_time_ms'], "ms")
154-
res += self.line_to_str(" read ", stats['rd_time_ms'], "ms")
155-
res += self.line_to_str(" total", stats['total_time_ms'], "ms")
156-
res += "Latency:\n"
157-
res += self.line_to_str(" min", stats['latency']["min_ns"], "ns")
158-
res += self.line_to_str(" max", stats['latency']["max_ns"], "ns")
159-
res += self.line_to_str(" avg", stats['latency']["avg_ns"], "ns")
160-
res += " histogram [ns]:\n"
161-
if len(stats['latency']['hist_ns']) > 0:
162-
prev = 0
163-
for k, v in stats['latency']['hist_ns'].items():
164-
if v != 0:
165-
res += self.line_to_str(f" {prev:> 6.1f} -{k:> 6.1f} ...", v)
166-
prev = k
167-
res += "Errors:\n"
168-
res += self.line_to_str(" zero burst count", stats['err_zero_burst'])
169-
res += self.line_to_str(" simultaneous r+w", stats['err_simult_rw'])
170-
171-
if self.config['VALUE_CNT'] > 1:
172-
res += "Paralel reads count:\n"
173-
res += self.line_to_str(" min", stats['paralel_read']["min"], "")
174-
res += self.line_to_str(" max", stats['paralel_read']["max"], "")
175-
res += self.line_to_str(" avg", stats['paralel_read']["avg"], "")
176-
177-
hist_step = self.config["HIST_STEP"][1]
178-
prev = 0
179-
for i, v in enumerate(stats["paralel_read"]["hist"]):
180-
if v != 0:
181-
res += self.line_to_str(f" {prev:> 6.1f} -{hist_step * (i + 1):> 6.1f} ...", v)
182-
prev = hist_step * (i + 1)
183-
184-
return res
185-
186-
def config_to_str(self):
187-
res = ""
188-
res += "Mem_logger config:\n"
189-
res += "------------------\n"
190-
res += f"MEM_DATA_WIDTH: {self.config['MEM_DATA_WIDTH']}\n"
191-
res += f"MEM_ADDR_WIDTH: {self.config['MEM_ADDR_WIDTH']}\n"
192-
res += f"MEM_BURST_WIDTH {self.config['MEM_BURST_WIDTH']}\n"
193-
res += f"MEM_FREQ_KHZ: {self.config['MEM_FREQ_KHZ']}\n"
194-
res += f"LATENCY_WIDTH: {self.config['VALUE_WIDTH'][0]}\n"
195-
res += f"HIST_BOX_CNT: {self.config['HIST_BOX_CNT'][0]}\n"
196-
res += f"HIST_BOX_WIDTH: {self.config['HIST_BOX_WIDTH'][0]}\n"
197-
res += "\n"
198-
return res
199-
200-
def print(self):
201-
print(self.config_to_str())
202-
203-
stats = self.load_stats()
204-
print(self.stats_to_str(stats))
159+
def calc_stats(self, data):
160+
data['Data flow']['total flow'] = np.array(data['Data flow']['wr flow']) + np.array(data['Data flow']['rd flow'])
161+
162+
return data
205163

206164

207165
def parseParams():
@@ -210,25 +168,39 @@ def parseParams():
210168
)
211169

212170
access = parser.add_argument_group('card access arguments')
213-
access.add_argument('-d', '--device', default=nfb.libnfb.Nfb.default_device,
214-
metavar='device', help="""device with target FPGA card""")
215-
access.add_argument('-i', '--index', type=int, metavar='index', default=0, help="""index inside DevTree""")
171+
access.add_argument(
172+
'-d', '--device', default=nfb.libnfb.Nfb.default_dev_path,
173+
metavar='device', help="""device with target FPGA card"""
174+
)
175+
access.add_argument(
176+
'-i', '--index', type=int, metavar='index',
177+
default=0, help="""index inside DevTree"""
178+
)
216179

217180
common = parser.add_argument_group('common arguments')
218-
#common.add_argument('-p', '--print', action='store_true', help = """print registers""")
219-
common.add_argument('--rst', action='store_true', help="""reset mem_tester and mem_logger""")
181+
common.add_argument(
182+
'--rst', action='store_true',
183+
help="""reset mem_tester and mem_logger"""
184+
)
185+
common.add_argument(
186+
'-j', '--stats-json', action='store_true',
187+
help="""prints mem_logger statistics in json"""
188+
)
220189
args = parser.parse_args()
221190
return args
222191

223192

224193
if __name__ == '__main__':
225194
args = parseParams()
226195
logger = MemLogger(dev=args.device, index=args.index)
196+
logger.stats.load()
227197

228-
#if args.print:
229-
logger.print()
198+
print(logger.stats.to_str())
230199

231200
if args.rst:
232201
logger.rst()
233202

203+
if args.stats_json:
204+
print(logger.stats.data())
205+
234206
#logger.set_config(latency_to_first=True)

0 commit comments

Comments
 (0)