ATLAS Offline Software
Loading...
Searching...
No Matches
CTPfragment.py
Go to the documentation of this file.
1# Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
2
3"""
4Minimal python module for CTP fragment access/modification
5
6For C++ implementation see:
7https://gitlab.cern.ch/atlas-tdaq-software/CTPfragment
8"""
9
10import sys
11import eformat
12import cppyy
13
14cppyy.load_library('libTrigByteStreamToolsDict')
15cppyy.load_library('libCTPfragment')
16from ROOT import CTPdataformat
17from ROOT import CTPfragment as _CTPfragment
18from ROOT.CTPfragment import getFolderUpdates # noqa: F401 (import into our namespace)
19
20# Import classes from C++ namespace
21FolderEntry = _CTPfragment.FolderEntry
22ExtraPayload = _CTPfragment.ExtraPayload
23
24def _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
35def _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
54def 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
66def 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
72def 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
77def 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
83def lumiBlock(rob):
84 return (rob.rod_detev_type() >> CTPdataformat.LumiBlockShift & CTPdataformat.LumiBlockMask)
85
86def setLumiBlock(rob, lb):
87 lbits = _setBits32(rob.rod_detev_type(), lb, CTPdataformat.LumiBlockShift, CTPdataformat.LumiBlockMask)
88 rob.rod_detev_type(lbits)
89
90def numberExtraPayloadWords(rob):
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
99def numberHltExtraPayloadWords(rob):
100 """Number extra payload words (this does NOT include the time since last L1A)"""
101 return max(numberExtraPayloadWords(rob)-1, 0)
102
103def hltExtraPayloadWords(rob):
104 n = numberHltExtraPayloadWords(rob)
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
110def getExtraPayloadObject(rob):
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
119def 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()]
125 n = numberHltExtraPayloadWords(rob)
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
147def lvl1AcceptBunch(rob):
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
157def 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
180def 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),
242 numberHltExtraPayloadWords(rob),
243 hltExtraPayloadWords(rob),
244 x.getL1PSK(),
245 x.getBGK(),
246 upd
247 ))
248
249if __name__ == "__main__":
250 sys.exit(main())
251
encodeTriggerBits(bits, len=1)
setCtpFormatVersion(rob, version)
_setBits32(bitset, value, shift, mask)
_versioned(obj, name, version)