Belle II Software  release-08-01-10
b2vcd_48.py
1 
8 
9 # =============================== b2vcd ===============================
10 # Convert Belle2Link data into human-readable Value Change Dump file
11 # =====================================================================
12 
13 from __future__ import print_function
14 import pickle
15 import re
16 from writer import VCDWriter
17 from bitstring import BitArray
18 import sys
19 import itertools
20 import ast
21 import operator as op
22 
23 if sys.version_info[0] < 3:
24  from itertools import izip as zip
25 period = 1 # data clock period is 32 (ns), but for the sake of convenience, let's use 1
26 
27 # checking whether all dummies don't contain any data. Turn 'False' to speed up
28 dummycheck = True
29 
30 
31 def printBin(evt, wordwidth=8, linewidth=8, paraheight=4):
32  words = [evt[word:word + wordwidth].bin for word in range(0, len(evt), wordwidth)]
33  lines = ([' '.join(words[n:n + linewidth]) for n in range(0, len(words), linewidth)])
34  paras = (['\n'.join(lines[n:n + paraheight]) for n in range(0, len(lines), paraheight)])
35  print('\n\n'.join(paras))
36 
37 
38 def printHex(evt, wordwidth=32, linewidth=8, paraheight=4):
39  words = [evt[word:word + wordwidth].hex for word in range(0, len(evt), wordwidth)]
40  lines = ([' '.join(words[n:n + linewidth]) for n in range(0, len(words), linewidth)])
41  paras = (['\n'.join(lines[n:n + paraheight]) for n in range(0, len(lines), paraheight)])
42  print('\n\n'.join(paras))
43 
44 
45 # example signal assignments
46 signalstsf2 = """
47 dddd(15 downto 0) & cntr125M(15 downto 0) &
48 hitMapTsf(191 downto 0) & valid_tracker(0 downto 0) &
49 unamed(23 downto 7) &
50 tracker_out[0](428 downto 210) & ccSelf(8 downto 0) &
51 unamed (77 downto 36) &
52 mergers[5](255 downto 236) & mergers[5](235 downto 0) &
53 mergers[4](255 downto 236) & mergers[4](235 downto 0) &
54 mergers[3](255 downto 236) & mergers[3](235 downto 0) &
55 mergers[2](255 downto 236) & mergers[2](235 downto 0) &
56 mergers[1](255 downto 236) & mergers[1](235 downto 0) &
57 mergers[0](255 downto 236) & mergers[0](235 downto 0)
58 """
59 
60 signalsnk = """
61 ddd(15 downto 0) & cntr125M2D(15 downto 0) &
62 "000" & TSF0_input(218 downto 210) &
63 "000" & TSF2_input(218 downto 210) &
64 "000" & TSF4_input(218 downto 210) &
65 "000" & TSF6_input(218 downto 210) &
66 "000" & TSF8_input(218 downto 210) &
67 "0000" &
68 """ + \
69  ''.join(["""TSF{sl:d}_input({high:d} downto {low:d}) &
70 TSF{sl:d}_input({high2:d} downto {low2:d}) &
71 """.format(sl=sl, high=h, low=h - 7, high2=h - 8, low2=h - 20) for sl in range(0, 9, 2) for h in range(209, 0, -21)]) + \
72  """unamed(901 downto 0)
73 """
74 
75 signalsall = signalsnk + signalstsf2
76 
77 # supported operators
78 operators = {ast.Add: op.add, ast.Sub: op.sub, ast.Mult: op.mul,
79  ast.Div: op.truediv, ast.Pow: op.pow, ast.USub: op.neg}
80 
81 
82 def eval_expr(expr):
83  return eval_(ast.parse(expr, mode='eval').body)
84 
85 
86 def eval_(node):
87  if isinstance(node, ast.Num): # <number>
88  return node.n
89  elif isinstance(node, ast.BinOp): # <left> <operator> <right>
90  return operators[type(node.op)](eval_(node.left), eval_(node.right))
91  elif isinstance(node, ast.UnaryOp): # <operator> <operand> e.g., -1
92  return operators[type(node.op)](eval_(node.operand))
93  else:
94  raise TypeError(node)
95 
96 
97 def makeAtlas(signals, evtsize):
98  """
99  Make bitmap from VHDL signal assignments.
100  input: string containing b2l signal designations.
101  output: list of lists
102  [ [ name, start, stop, pos, size, module ], ... ]
103  """
104  atlas = []
105  pos = 0
106  finesses = re.findall(r'(.+?)<=(.+?);', signals, re.DOTALL)
107  if len(finesses) != sum(1 if width > 0 else 0 for width in evtsize):
108  raise Exception('Number of valid signal assignments does not match HSLB data dimension: ' + str(evtsize))
109  for finesse in finesses:
110  for item in re.split(r'[\s\n]?&[\s\n]*', finesse[1].strip()):
111  item = re.sub(r'--.+', '', item)
112  dummy = re.match(r'"([01]+)"', item)
113  if not dummy:
114  sig = re.match(r'(.+)\s*\‍((.+) downto (.+)\‍)', item.strip())
115  if not sig:
116  print('continuing')
117  continue
118  print('signal: ' + item.strip())
119  start = eval_expr(sig.group(2))
120  stop = eval_expr(sig.group(3))
121  size = start - stop + 1
122  if size < 1:
123  raise ValueError('Vector size cannot be 0 or negative.')
124  name = sig.group(1)
125  else:
126  size = len(dummy.group(1))
127  name = 'unamed'
128  start, stop = size - 1, 0
129  atlas.append([name, start, stop, pos, size, finesse[0]])
130  pos += size
131  if pos != sum(evtsize):
132  raise ValueError(
133  'Size of atlas:{} does not match event size:{}'.format(pos, evtsize))
134  return atlas
135 
136 
137 def isUnamed(signal):
138  return signal[0] == "unamed"
139 
140 
141 def unpack(meta, headers, triggers, atlas, writer, evtsize):
142  """
143  transform into VCD signals from clock-seperated data and hitmap
144  """
145  unpackwith = ', '.join(['bin:{}'.format(sig[4]) for sig in atlas])
146  vcdVars = [writer.register_var(
147  sig[5], sig[0] + '[{}:{}]'.format(sig[1], sig[2]), 'wire', size=sig[4]) if
148  not isUnamed(sig) else None for sig in atlas]
149  event = writer.register_var('m', 'event', 'event')
150  eventNo = writer.register_var('m', 'eventNo', 'integer')
151  subrun = writer.register_var('m', 'subrun', 'integer')
152  run = writer.register_var('m', 'run', 'integer')
153  delays = [writer.register_var('m', 'delay{}'.format(i), 'wire', size=9)
154  for i in range(len(evtsize))]
155  lastvalues = [None] * len(atlas)
156  iteration = 0
157  timestamp = 0
158  power = period.bit_length() - 1 # (marginal and ugly) speed optimization
159  print('converting to waveform ...')
160 
161  # null = BitArray([0] * sum(evtsize))
162 
163  change = writer.change
164  for trgInd, trg in enumerate(triggers):
165  # skip empty (dummy) buffers
166  # if trg[:16].hex == 'dddd':
167  # continue
168  # print(trgInd)
169  # printHex(trg[0])
170  # printHex(trg)
171 
172  timestamp = iteration << power
173  writer.change(event, timestamp, '1')
174  # writer.change(eventNo, timestamp, trgInd)
175  writer.change(eventNo, timestamp, meta[trgInd][0])
176  writer.change(run, timestamp, meta[trgInd][1])
177  writer.change(subrun, timestamp, meta[trgInd][2])
178  for i, d in enumerate(delays):
179  if len(headers[trgInd][i]) <= 0:
180  continue
181  writer.change(d, timestamp, headers[trgInd][i][64 + 11: 64 + 20].uint)
182  clocks = list(trg.cut(sum(evtsize)))
183  # recover clock shifts
184  if len(clocks) > 47:
185  clocks = clocks[1:48] + [clocks[0]]
186  # clocks = clocks[2:48] + clocks[0:2]
187  # for clock in trg.cut(sum(evtsize)):
188  for clock in clocks:
189  if timestamp & 1023 == 0:
190  print('\r{} us converted'.format(str(timestamp)[:-3]), end="")
191  sys.stdout.flush()
192  timestamp = iteration << power
193  iteration += 1
194  values = clock.unpack(unpackwith)
195  for i, sig in enumerate(atlas):
196  if isUnamed(sig):
197  if dummycheck and '1' in values[i] and '0' in values[i]:
198  print('[Warning] non-uniform dummy value detected at {}ns: {}'.format(
199  timestamp, sig[3] % sum(evtsize)))
200  print(values[i])
201  continue
202  if values[i] != lastvalues[i]:
203  change(vcdVars[i], timestamp, values[i])
204  lastvalues = values
205 
206  # filling 16clk gaps
207  # timestamp = iteration << power
208  # iteration += 6
209  # values = null.unpack(unpackwith)
210  # for i, sig in enumerate(atlas):
211  # if isUnamed(sig):
212  # continue
213  # writer.change(vcdVars[i], timestamp, values[i])
214  print('')
215 
216 
217 def writeVCD(meta, data, atlas, fname, evtsize, combine=False):
218  """
219  evt: 2D list of HSLB buffers
220  atlas: list of iterates
221  """
222  # combine data in different buffers into a single block of event size
223  samples = len(list(filter(None, data[0]))[0]) // list(filter(None, evtsize))[0]
224  NullArray = BitArray([])
225  # clocks = BitArray([])
226  clocks = []
227  headers = []
228  for evt in data:
229  clocks.append(BitArray([]).join(
230  itertools.chain.from_iterable(zip(
231  # skip b2l header (3 words)
232  *[evt[buf][32 * 3:].cut(evtsize[buf])
233  if evtsize[buf] > 0 else [NullArray] * samples
234  # for buf in range(4)]))))
235  for buf in range(len(evtsize))]))))
236  headers.append([evt[buf][:32 * 3] for buf in range(len(evtsize))])
237  with open(fname, 'w') as fout:
238  with VCDWriter(fout, timescale='1 ns', date='today') as writer:
239  if combine:
240  # combVCD(clocks, atlas, writer)
241  raise Exception('Combine has not been updated. Use literal instead.')
242  else:
243  # literalVCD(clocks, atlas, writer)
244  unpack(meta, headers, clocks, atlas, writer, evtsize)
245 
246 
247 if __name__ == "__main__":
248  import argparse
249  parser = argparse.ArgumentParser()
250  parser.add_argument("-i", "--pickle", help="input pickle file")
251  parser.add_argument("-s", "--signal", help="input signal file")
252  parser.add_argument("-o", "--output", help="output VCD file")
253  parser.add_argument("-nc", "--nocheck", help="disable dummy variable check",
254  action="store_true")
255  args = parser.parse_args()
256  if args.signal:
257  with open(args.signal) as fin:
258  evtsize = [int(width) for width in fin.readline().split()]
259  print('interpreting B2L data with dimension ' + str(evtsize))
260  atlas = makeAtlas(fin.read(), evtsize)
261  else:
262  atlas = makeAtlas(signalsall, evtsize)
263 
264  pica = args.pickle
265  rfp = open(pica, 'rb')
266  data = pickle.load(rfp)
267  meta = pickle.load(rfp)
268  rfp.close()
269 
270  if args.nocheck:
271  dummycheck = False
272  fname = args.output if args.output else pica[:pica.rfind('.')] + '.vcd'
273  writeVCD(meta, data, atlas, fname, evtsize)
std::map< ExpRun, std::pair< double, double > > filter(const std::map< ExpRun, std::pair< double, double >> &runs, double cut, std::map< ExpRun, std::pair< double, double >> &runsRemoved)
filter events to remove runs shorter than cut, it stores removed runs in runsRemoved
Definition: Splitter.cc:38
Definition: unpack.py:1