ATLAS Offline Software
CTPfragment.py
Go to the documentation of this file.
1 # Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
2 
3 """
4 Minimal python module for CTP fragment access/modification
5 
6 For C++ implementation see:
7 https://gitlab.cern.ch/atlas-tdaq-software/CTPfragment
8 """
9 
10 import sys
11 import eformat
12 import cppyy
13 
14 cppyy.load_library('libTrigByteStreamToolsDict')
15 cppyy.load_library('libCTPfragment')
16 from ROOT import CTPdataformat
17 from ROOT import CTPfragment as _CTPfragment
18 from ROOT.CTPfragment import getFolderUpdates # noqa: F401 (import into our namespace)
19 
20 # Import classes from C++ namespace
21 FolderEntry = _CTPfragment.FolderEntry
22 ExtraPayload = _CTPfragment.ExtraPayload
23 
24 def _versioned(obj, name, version):
25  """Helper to return versioned members of CTPdataformat"""
26  attr = ''
27  v = version
28  while v>=0: # decrement version until found
29  attr = '%s_v%d' % (name, v)
30  if hasattr(obj,attr):
31  break
32  v -= 1
33  return getattr(obj,attr)
34 
35 def _setBits32(bitset, value, shift, mask):
36  """Set value in bitset using mask and shifting n bits to the left"""
37  v = bitset & (0xffffffff ^ (mask << shift))
38  v = v | (value << shift)
39  return v
40 
42  """Return list of bits [0,1,1,0,...] from list of 32-bit trigger words"""
43  if type(info)==int:
44  info=[info]
45  bits=[]
46  cnt=0
47  for word in info:
48  for i in range(32):
49  if word&(1<<i):
50  bits+=[cnt]
51  cnt+=1
52  return bits
53 
54 def encodeTriggerBits(bits,len=1):
55  """Return list of trigger words from list of bits"""
56  words = [0] * len
57  for bit in bits:
58  words[bit//32] |= 1<<(bit%32)
59  return words
60 
62  """Get CTP fragment format version"""
63  v = (rob.rod_minor_version() >> CTPdataformat.CTPFormatVersionShift) & CTPdataformat.CTPFormatVersionMask
64  return v
65 
66 def setCtpFormatVersion(rob, version):
67  """Set CTP fragment format version"""
68  V = _setBits32(rob.rod_minor_version(), version,
69  CTPdataformat.CTPFormatVersionShift, CTPdataformat.CTPFormatVersionMask)
70  rob.rod_minor_version(V)
71 
72 def hltCounter(rob):
73  """Get HLT counter"""
74  v = ctpFormatVersion(rob)
75  return (rob.rod_detev_type() >> _versioned(CTPdataformat,'HltCounterShift',v) & _versioned(CTPdataformat,'HltCounterMask',v))
76 
77 def setHltCounter(rob, hltCounter):
78  """Set HLT counter"""
79  v = ctpFormatVersion(rob)
80  c = _setBits32(rob.rod_detev_type(), hltCounter, _versioned(CTPdataformat,'HltCounterShift',v), _versioned(CTPdataformat,'HltCounterMask',v))
81  rob.rod_detev_type(c)
82 
83 def lumiBlock(rob):
84  return (rob.rod_detev_type() >> CTPdataformat.LumiBlockShift & CTPdataformat.LumiBlockMask)
85 
86 def setLumiBlock(rob, lb):
87  lbits = _setBits32(rob.rod_detev_type(), lb, CTPdataformat.LumiBlockShift, CTPdataformat.LumiBlockMask)
88  rob.rod_detev_type(lbits)
89 
91  """Number extra payload words (this includes the time since last L1A)"""
92  v = ctpFormatVersion(rob)
93  if v < 2:
94  return 0
95  else:
96  return ((rob.rod_minor_version() >> _versioned(CTPdataformat,'ProgrammableExtraWordsShift',v)
97  & _versioned(CTPdataformat,'ProgrammableExtraWordsMask',v)))
98 
100  """Number extra payload words (this does NOT include the time since last L1A)"""
101  return max(numberExtraPayloadWords(rob)-1, 0)
102 
105  offset = 0
106  if ctpFormatVersion(rob)>=3:
107  offset = 1 # for turn counter
108  return rob.rod_data()[-numberHltExtraPayloadWords(rob)+offset:] if n>0 else []
109 
111  """Return CTPfragment::ExtraPayload object created from CTP ROB"""
112  v = cppyy.gbl.std.vector('unsigned int')()
113  for p in hltExtraPayloadWords(rob):
114  v.push_back(p)
115 
116  x = _CTPfragment.ExtraPayload(v)
117  return x
118 
119 def setHltExtraPayloadWords(rob, extraWords):
120 
121  # Make writable ROB
122  wrob = eformat.write.ROBFragment(rob)
123  # Copy ROD data, except extra words
124  data = [d for d in wrob.rod_data()]
126  if n>0:
127  data = data[:-n]
128  # Append new extra words and set in ROB
129  v = ctpFormatVersion(wrob)
130  if v<2:
131  setCtpFormatVersion(wrob, 2) # need at least V2
132  data.extend([0]) # time since last L1A
133  data.extend(extraWords)
134  wrob.rod_data(data)
135 
136  # Set new payload length (including words reserved by CTP)
137  ctp_extras = 1 # time since last L1A
138  if v>=3:
139  ctp_extras = 2 # turn counter
140  V = _setBits32(wrob.rod_minor_version(), len(extraWords)+ctp_extras,
141  _versioned(CTPdataformat,'ProgrammableExtraWordsShift',v),
142  _versioned(CTPdataformat,'ProgrammableExtraWordsMask',v))
143  wrob.rod_minor_version(V)
144 
145  return wrob.readonly()
146 
148  """Get position of LVL1-Accept Bunch from ROD Fragment"""
149 
150  v = ctpFormatVersion(rob)
151  shift = _versioned(CTPdataformat,'L1APositionShift',v)
152  if v==0:
153  return (rob.rod_detev_type() >> shift) & CTPdataformat.L1APositionMask
154  else:
155  return (rob.rod_minor_version() >> shift) & CTPdataformat.L1APositionMask
156 
157 def getTriggerWords(rob,name='TAV'):
158  """Get trigger words"""
159 
160  name = name.upper()
161  v = ctpFormatVersion(rob)
162  pos = _versioned(CTPdataformat,name+'pos',min(v,4))
163  words = _versioned(CTPdataformat,name+'words',min(v,4))
164  # No TAP in RoI ROB
165  if v>4 and rob.source_id().module_id()==1:
166  pos = _versioned(CTPdataformat,name+'pos',5)
167  if name=='TAP':
168  return []
169 
170  l1abunch = 0 if rob.source_id().module_id()==1 else lvl1AcceptBunch(rob)
171 
172  data = [d for d in rob.rod_data()]
173  pos = l1abunch*_versioned(CTPdataformat,'DAQwordsPerBunch',v) + pos
174  return data[pos:pos+words]
175 
176 #
177 # For testing
178 #
179 
180 def main():
181  from optparse import OptionParser
182  import eformat
183 
184  parser = OptionParser(usage='%prog FILE')
185  parser.add_option('-m', '--moduleid', type='int', action='store', default=0,
186  help='Module ID of CTP fragment [%default]')
187 
188  (opt, args) = parser.parse_args()
189  if len(args)!=1:
190  parser.print_help()
191  return 1
192 
193  for event in eformat.istream(args[0]):
194  ctp_robs = [rob for rob in event.children()
195  if rob.source_id().subdetector_id() == eformat.helper.SubDetector.TDAQ_CTP
196  and rob.source_id().module_id() == opt.moduleid]
197 
198  if len(ctp_robs)==0:
199  print("Cannot find CTP ROB with module ID %d" % opt.moduleid)
200  continue
201 
202  rob = ctp_robs[0]
203  fe = _CTPfragment.FolderEntry()
204  fe.folderIndex = 1
205  fe.lumiBlock = 54
206 
207  fe2 = _CTPfragment.FolderEntry()
208  fe2.folderIndex = 2
209  fe2.lumiBlock = 59
210 
211  #x = getExtraPayloadObject(rob)
212  x = _CTPfragment.ExtraPayload()
213  x.setL1PSK(255)
214  x.updateFolder(fe)
215  x.updateFolder(fe2)
216  new_ctp_rob = setHltExtraPayloadWords(rob, [d for d in x.serialize()])
217  new_event = eformat.write.FullEventFragment()
218  new_event.copy_header(event)
219  for r in event.children():
220  if r.source_id().subdetector_id() != eformat.helper.SubDetector.TDAQ_CTP:
221  new_event.append(eformat.write.ROBFragment(r))
222 
223  new_event.append(eformat.write.ROBFragment(new_ctp_rob))
224 
225  event = new_event.readonly()
226  #new_ctp_rob = eformat.write.ROBFragment(new_ctp_rob)
227  #setHltCounter(new_ctp_rob,100)
228  rob = new_ctp_rob
229 
230  x = getExtraPayloadObject(rob)
231  folderUpdates = _CTPfragment.getFolderUpdates(x)
232  upd = ''
233  for f in folderUpdates:
234  upd += ('[%d,%d]' % (f.second.folderIndex,f.second.lumiBlock))
235 
236  print("L1ID %10d, LB %4d, Version %d, Bunch %d, HLT counter: %3d, Payload #%d %s L1PSK %d BGK %d COOLUPD %s" % (
237  event.lvl1_id(),
238  event.lumi_block(),
239  ctpFormatVersion(rob),
240  lvl1AcceptBunch(rob),
241  hltCounter(rob),
244  x.getL1PSK(),
245  x.getBGK(),
246  upd
247  ))
248 
249 if __name__ == "__main__":
250  sys.exit(main())
251 
python.CTPfragment.setHltExtraPayloadWords
def setHltExtraPayloadWords(rob, extraWords)
Definition: CTPfragment.py:119
max
constexpr double max()
Definition: ap_fixedTest.cxx:33
min
constexpr double min()
Definition: ap_fixedTest.cxx:26
python.CTPfragment._versioned
def _versioned(obj, name, version)
Definition: CTPfragment.py:24
python.CTPfragment.numberExtraPayloadWords
def numberExtraPayloadWords(rob)
Definition: CTPfragment.py:90
python.CaloAddPedShiftConfig.type
type
Definition: CaloAddPedShiftConfig.py:42
python.CTPfragment.setCtpFormatVersion
def setCtpFormatVersion(rob, version)
Definition: CTPfragment.py:66
python.CTPfragment.getExtraPayloadObject
def getExtraPayloadObject(rob)
Definition: CTPfragment.py:110
python.CTPfragment._setBits32
def _setBits32(bitset, value, shift, mask)
Definition: CTPfragment.py:35
python.CTPfragment.getTriggerWords
def getTriggerWords(rob, name='TAV')
Definition: CTPfragment.py:157
plotBeamSpotVxVal.range
range
Definition: plotBeamSpotVxVal.py:194
python.CTPfragment.main
def main()
Definition: CTPfragment.py:180
python.CTPfragment.setHltCounter
def setHltCounter(rob, hltCounter)
Definition: CTPfragment.py:77
python.CTPfragment.decodeTriggerBits
def decodeTriggerBits(info)
Definition: CTPfragment.py:41
print
void print(char *figname, TCanvas *c1)
Definition: TRTCalib_StrawStatusPlots.cxx:25
python.CTPfragment.numberHltExtraPayloadWords
def numberHltExtraPayloadWords(rob)
Definition: CTPfragment.py:99
python.CTPfragment.ctpFormatVersion
def ctpFormatVersion(rob)
Definition: CTPfragment.py:61
python.CTPfragment.setLumiBlock
def setLumiBlock(rob, lb)
Definition: CTPfragment.py:86
python.CTPfragment.lvl1AcceptBunch
def lvl1AcceptBunch(rob)
Definition: CTPfragment.py:147
python.CTPfragment.hltCounter
def hltCounter(rob)
Definition: CTPfragment.py:72
python.CTPfragment.hltExtraPayloadWords
def hltExtraPayloadWords(rob)
Definition: CTPfragment.py:103
python.CTPfragment.encodeTriggerBits
def encodeTriggerBits(bits, len=1)
Definition: CTPfragment.py:54
python.CTPfragment.lumiBlock
def lumiBlock(rob)
Definition: CTPfragment.py:83