Belle II Software  release-06-00-14
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 # supported operators
46 operators = {ast.Add: op.add, ast.Sub: op.sub, ast.Mult: op.mul,
47  ast.Div: op.truediv, ast.Pow: op.pow, ast.USub: op.neg}
48 
49 
50 def eval_expr(expr):
51  return eval_(ast.parse(expr, mode='eval').body)
52 
53 
54 def eval_(node):
55  if isinstance(node, ast.Num): # <number>
56  return node.n
57  elif isinstance(node, ast.BinOp): # <left> <operator> <right>
58  return operators[type(node.op)](eval_(node.left), eval_(node.right))
59  elif isinstance(node, ast.UnaryOp): # <operator> <operand> e.g., -1
60  return operators[type(node.op)](eval_(node.operand))
61  else:
62  raise TypeError(node)
63 
64 
65 def makeAtlas(signals, evtsize):
66  """
67  Make bitmap from VHDL signal assignments.
68  input: string containing b2l signal designations.
69  output: list of lists
70  [ [ name, start, stop, pos, size, module ], ... ]
71  """
72  atlas = []
73  pos = 0
74  finesses = re.findall(r'(.+?)<=(.+?);', signals, re.DOTALL)
75  if len(finesses) != sum(1 if width > 0 else 0 for width in evtsize):
76  raise Exception('Number of valid signal assignments does not match HSLB data dimension: ' + str(evtsize))
77  for finesse in finesses:
78  for item in re.split(r'[\s\n]?&[\s\n]*', finesse[1].strip()):
79  item = re.sub(r'--.+', '', item)
80  dummy = re.match(r'"([01]+)"', item)
81  if not dummy:
82  sig = re.match(r'(.+)\s*\‍((.+) downto (.+)\‍)', item.strip())
83  if not sig:
84  print('continuing')
85  continue
86  print('signal: ' + item.strip())
87  start = eval_expr(sig.group(2))
88  stop = eval_expr(sig.group(3))
89  size = start - stop + 1
90  if size < 1:
91  raise ValueError('Vector size cannot be 0 or negative.')
92  name = sig.group(1)
93  else:
94  size = len(dummy.group(1))
95  name = 'unamed'
96  start, stop = size - 1, 0
97  atlas.append([name, start, stop, pos, size, finesse[0]])
98  pos += size
99  if pos != sum(evtsize):
100  raise ValueError(
101  'Size of atlas:{} does not match event size:{}'.format(pos, evtsize))
102  return atlas
103 
104 
105 def isUnamed(signal):
106  return signal[0] == "unamed"
107 
108 
109 def unpack(meta, headers, triggers, atlas, writer, evtsize):
110  """
111  transform into VCD signals from clock-seperated data and hitmap
112  """
113  unpackwith = ', '.join(['bin:{}'.format(sig[4]) for sig in atlas])
114  vcdVars = [writer.register_var(
115  sig[5], sig[0] + '[{}:{}]'.format(sig[1], sig[2]), 'wire', size=sig[4]) if
116  not isUnamed(sig) else None for sig in atlas]
117  event = writer.register_var('m', 'event', 'event')
118  eventNo = writer.register_var('m', 'eventNo', 'integer')
119  subrun = writer.register_var('m', 'subrun', 'integer')
120  run = writer.register_var('m', 'run', 'integer')
121  delays = [writer.register_var('m', 'delay{}'.format(i), 'wire', size=9)
122  for i in range(len(evtsize))]
123  lastvalues = [None] * len(atlas)
124  iteration = 0
125  timestamp = 0
126  power = period.bit_length() - 1 # (marginal and ugly) speed optimization
127  print('converting to waveform ...')
128 
129  null = BitArray([0] * sum(evtsize))
130 
131  change = writer.change
132  for trgInd, trg in enumerate(triggers):
133  # skip empty (dummy) buffers
134  # if trg[:16].hex == 'dddd':
135  # continue
136  # print(trgInd)
137  # printHex(trg[0])
138  # printHex(trg)
139 
140  timestamp = iteration << power
141  writer.change(event, timestamp, '1')
142  # writer.change(eventNo, timestamp, trgInd)
143  writer.change(eventNo, timestamp, meta[trgInd][0])
144  writer.change(run, timestamp, meta[trgInd][1])
145  writer.change(subrun, timestamp, meta[trgInd][2])
146  for i, d in enumerate(delays):
147  if len(headers[trgInd][i]) <= 0:
148  continue
149  writer.change(d, timestamp, headers[trgInd][i][64 + 11: 64 + 20].uint)
150  clocks = list(trg.cut(sum(evtsize)))
151  # recover clock shifts
152  if len(clocks) > 47:
153  clocks = clocks[1:48] + [clocks[0]]
154  # clocks = clocks[2:48] + clocks[0:2]
155  # for clock in trg.cut(sum(evtsize)):
156  for clock in clocks:
157  if timestamp & 1023 == 0:
158  print('\r{} us converted'.format(str(timestamp)[:-3]), end="")
159  sys.stdout.flush()
160  timestamp = iteration << power
161  iteration += 1
162  values = clock.unpack(unpackwith)
163  for i, sig in enumerate(atlas):
164  if isUnamed(sig):
165  if dummycheck and '1' in values[i] and '0' in values[i]:
166  print('[Warning] non-uniform dummy value detected at {}ns: {}'.format(
167  timestamp, sig[3] % sum(evtsize)))
168  print(values[i])
169  continue
170  if values[i] != lastvalues[i]:
171  change(vcdVars[i], timestamp, values[i])
172  lastvalues = values
173 
174  # filling 16clk gaps
175  # timestamp = iteration << power
176  # iteration += 6
177  # values = null.unpack(unpackwith)
178  # for i, sig in enumerate(atlas):
179  # if isUnamed(sig):
180  # continue
181  # writer.change(vcdVars[i], timestamp, values[i])
182  print('')
183 
184 
185 def writeVCD(meta, data, atlas, fname, evtsize, combine=False):
186  """
187  evt: 2D list of HSLB buffers
188  atlas: list of iterates
189  """
190  # combine data in different buffers into a single block of event size
191  samples = len(list(filter(None, data[0]))[0]) // list(filter(None, evtsize))[0]
192  NullArray = BitArray([])
193  # clocks = BitArray([])
194  clocks = []
195  headers = []
196  for evt in data:
197  clocks.append(BitArray([]).join(
198  itertools.chain.from_iterable(zip(
199  # skip b2l header (3 words)
200  *[evt[buf][32 * 3:].cut(evtsize[buf])
201  if evtsize[buf] > 0 else [NullArray] * samples
202  # for buf in range(4)]))))
203  for buf in range(len(evtsize))]))))
204  headers.append([evt[buf][:32 * 3] for buf in range(len(evtsize))])
205  with open(fname, 'w') as fout:
206  with VCDWriter(fout, timescale='1 ns', date='today') as writer:
207  if combine:
208  # combVCD(clocks, atlas, writer)
209  raise Exception('Combine has not been updated. Use literal instead.')
210  else:
211  # literalVCD(clocks, atlas, writer)
212  unpack(meta, headers, clocks, atlas, writer, evtsize)
213 
214 
215 if __name__ == "__main__":
216  import argparse
217  parser = argparse.ArgumentParser()
218  parser.add_argument("-i", "--pickle", help="input pickle file")
219  parser.add_argument("-s", "--signal", help="input signal file")
220  parser.add_argument("-o", "--output", help="output VCD file")
221  parser.add_argument("-nc", "--nocheck", help="disable dummy variable check",
222  action="store_true")
223  args = parser.parse_args()
224  if args.signal:
225  with open(args.signal) as fin:
226  evtsize = [int(width) for width in fin.readline().split()]
227  print('interpreting B2L data with dimension ' + str(evtsize))
228  atlas = makeAtlas(fin.read(), evtsize)
229  else:
230  atlas = makeAtlas(signalsall, evtsize)
231 
232  pica = args.pickle
233  rfp = open(pica, 'rb')
234  data = pickle.load(rfp)
235  meta = pickle.load(rfp)
236  rfp.close()
237 
238  if args.nocheck:
239  dummycheck = False
240  fname = args.output if args.output else pica[:pica.rfind('.')] + '.vcd'
241  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:40
Definition: unpack.py:1