LDMX Software
hitproducer_hw.cxx
1
2
3#include "TrigScint/Firmware/hitproducer.h"
4#include "TrigScint/Firmware/objdef.h"
5
6void hitproducerHw(ap_uint<14> FIFO[NHITS][5], Hit outHit[NHITS],
7 ap_uint<8> Peds[NHITS]) {
8#ifdef TS_NOT_EMULATION
9#pragma HLS ARRAY_PARTITION variable = FIFO complete
10#pragma HLS ARRAY_PARTITION variable = amplitude complete
11#pragma HLS ARRAY_PARTITION variable = Peds complete
12#pragma HLS INTERFACE ap_fifo depth = 16 port = FIFO[0]
13#pragma HLS INTERFACE ap_fifo depth = 16 port = FIFO[1]
14#pragma HLS INTERFACE ap_fifo depth = 16 port = FIFO[2]
15#pragma HLS INTERFACE ap_fifo depth = 16 port = FIFO[3]
16#pragma HLS INTERFACE ap_fifo depth = 16 port = FIFO[4]
17#pragma HLS INTERFACE ap_fifo depth = 16 port = FIFO[5]
18#pragma HLS INTERFACE ap_fifo depth = 16 port = FIFO[6]
19#pragma HLS INTERFACE ap_fifo depth = 16 port = FIFO[7]
20#pragma HLS INTERFACE ap_fifo depth = 16 port = FIFO[8]
21#pragma HLS INTERFACE ap_fifo depth = 16 port = FIFO[9]
22#pragma HLS INTERFACE ap_fifo depth = 16 port = FIFO[10]
23#pragma HLS INTERFACE ap_fifo depth = 16 port = FIFO[11]
24#pragma HLS INTERFACE ap_fifo depth = 16 port = FIFO[12]
25#pragma HLS INTERFACE ap_fifo depth = 16 port = FIFO[13]
26#pragma HLS INTERFACE ap_fifo depth = 16 port = FIFO[14]
27#pragma HLS INTERFACE ap_fifo depth = 16 port = FIFO[15]
28#pragma HLS INTERFACE ap_fifo depth = 16 port = FIFO[16]
29#pragma HLS INTERFACE ap_fifo depth = 16 port = FIFO[17]
30#pragma HLS INTERFACE ap_fifo depth = 16 port = FIFO[18]
31#pragma HLS INTERFACE ap_fifo depth = 16 port = FIFO[19]
32
33#pragma HLS INTERFACE ap_fifo depth = 16 port = FIFO[20]
34#pragma HLS INTERFACE ap_fifo depth = 16 port = FIFO[21]
35#pragma HLS INTERFACE ap_fifo depth = 16 port = FIFO[22]
36#pragma HLS INTERFACE ap_fifo depth = 16 port = FIFO[23]
37#pragma HLS INTERFACE ap_fifo depth = 16 port = FIFO[24]
38#pragma HLS INTERFACE ap_fifo depth = 16 port = FIFO[25]
39#pragma HLS INTERFACE ap_fifo depth = 16 port = FIFO[26]
40#pragma HLS INTERFACE ap_fifo depth = 16 port = FIFO[27]
41#pragma HLS INTERFACE ap_fifo depth = 16 port = FIFO[28]
42#pragma HLS INTERFACE ap_fifo depth = 16 port = FIFO[29]
43
44#pragma HLS INTERFACE ap_fifo depth = 16 port = FIFO[30]
45#pragma HLS INTERFACE ap_fifo depth = 16 port = FIFO[31]
46#pragma HLS INTERFACE ap_fifo depth = 16 port = FIFO[32]
47#pragma HLS INTERFACE ap_fifo depth = 16 port = FIFO[33]
48#pragma HLS INTERFACE ap_fifo depth = 16 port = FIFO[34]
49#pragma HLS INTERFACE ap_fifo depth = 16 port = FIFO[35]
50#pragma HLS INTERFACE ap_fifo depth = 16 port = FIFO[36]
51#pragma HLS INTERFACE ap_fifo depth = 16 port = FIFO[37]
52#pragma HLS INTERFACE ap_fifo depth = 16 port = FIFO[38]
53#pragma HLS INTERFACE ap_fifo depth = 16 port = FIFO[39]
54
55#pragma HLS INTERFACE ap_fifo depth = 16 port = FIFO[40]
56#pragma HLS INTERFACE ap_fifo depth = 16 port = FIFO[41]
57#pragma HLS INTERFACE ap_fifo depth = 16 port = FIFO[42]
58#pragma HLS INTERFACE ap_fifo depth = 16 port = FIFO[43]
59#pragma HLS INTERFACE ap_fifo depth = 16 port = FIFO[44]
60#pragma HLS INTERFACE ap_fifo depth = 16 port = FIFO[45]
61#pragma HLS INTERFACE ap_fifo depth = 16 port = FIFO[46]
62#pragma HLS INTERFACE ap_fifo depth = 16 port = FIFO[47]
63
64#pragma HLS INTERFACE ap_fifo depth = 16 port = FIFO[48]
65#pragma HLS INTERFACE ap_fifo depth = 16 port = FIFO[49]
66
67#pragma HLS PIPELINE
68#endif
69
70 // The QIE11 card takes an analogue SiPM PE count
71 // and converts electron counts from it via a piecewise
72 // exponential curve into an ADC. Depending on the shunts
73 // you use, you can affect the gain; the gains and variable
74 // values determined here are motived primarily by those required
75 // to get the MIP distribution seen in the 2022 beam.
76 // The next variables show where each linear portion of the
77 // exponential map start in charge count (edges_) and their slope;
78 // the hitmaker delinearized the adc counts, integrates over five clockcycles
79 // and forms a hit.
80
82 ap_uint<14> nbins[5] = {0, 16, 36, 57, 64};
83
85 ap_uint<14> edges[17] = {0, 34, 158, 419, 517, 915,
86 1910, 3990, 4780, 7960, 15900, 32600,
87 38900, 64300, 128000, 261000, 350000};
89 ap_uint<14> sense[16] = {3, 6, 12, 25, 25, 50, 99, 198,
90 198, 397, 794, 1587, 1587, 3174, 6349, 12700};
91
92 for (int i = 0; i < NHITS; i++) {
93 outHit[i].b_id_ = -1;
94 outHit[i].m_id_ = 0;
95 outHit[i].time_ = 0;
96 outHit[i].amp_ = 0;
97 ap_uint<14> word1 = FIFO[i][0];
98 ap_uint<14> word2 = FIFO[i][1];
99 ap_uint<14> word3 = FIFO[i][2];
100 ap_uint<14> word4 = FIFO[i][3];
101 ap_uint<14> word5 = FIFO[i][4];
102 ap_uint<16> charge1;
103 ap_uint<16> charge2;
104 ap_uint<16> charge3;
105 ap_uint<16> charge4;
106 ap_uint<16> charge5;
107 ap_uint<4> shunt = 1;
108 // An identical procedure is used for all 5 clockcylces. Namely you extract
109 // the adc value from the adc+tdc concatenated value you get from the raw
110 // strwam via (word1>>6); You then use what integer multiple of 64 it is to
111 // determine which linear segment you are on, and v1 (the remainder) to
112 // determine how far along that linear segment your charge carried you.
113 // Together that gets you charge.
114
115 ap_uint<14> rr = (word1 >> 6) / 64;
116 ap_uint<14> v1 = (word1 >> 6) % 64;
117 ap_uint<14> ss =
118 1 * (v1 > nbins[1]) + 1 * (v1 > nbins[2]) + 1 * (v1 > nbins[3]);
119 charge1 = edges[4 * rr + ss] + (v1 - nbins[ss]) * sense[4 * rr + ss] +
120 sense[4 * rr + ss] / 2 - 1;
121
122 rr = (word2 >> 6) / 64;
123 v1 = (word2 >> 6) % 64;
124 ss = 1 * (v1 > nbins[1]) + 1 * (v1 > nbins[2]) + 1 * (v1 > nbins[3]);
125 charge2 = edges[4 * rr + ss] + (v1 - nbins[ss]) * sense[4 * rr + ss] +
126 sense[4 * rr + ss] / 2 - 1;
127
128 rr = (word3 >> 6) / 64;
129 v1 = (word3 >> 6) % 64;
130 ss = 1 * (v1 > nbins[1]) + 1 * (v1 > nbins[2]) + 1 * (v1 > nbins[3]);
131 charge3 = edges[4 * rr + ss] + (v1 - nbins[ss]) * sense[4 * rr + ss] +
132 sense[4 * rr + ss] / 2 - 1;
133
134 rr = (word4 >> 6) / 64;
135 v1 = (word4 >> 6) % 64;
136 ss = 1 * (v1 > nbins[1]) + 1 * (v1 > nbins[2]) + 1 * (v1 > nbins[3]);
137 charge4 = edges[4 * rr + ss] + (v1 - nbins[ss]) * sense[4 * rr + ss] +
138 sense[4 * rr + ss] / 2 - 1;
139
140 rr = (word5 >> 6) / 64;
141 v1 = (word5 >> 6) % 64;
142 ss = 1 * (v1 > nbins[1]) + 1 * (v1 > nbins[2]) + 1 * (v1 > nbins[3]);
143 charge5 = edges[4 * rr + ss] + (v1 - nbins[ss]) * sense[4 * rr + ss] +
144 sense[4 * rr + ss] / 2 - 1;
145
146 outHit[i].b_id_ = i;
147
148 // You now are creating an output hit. The time of the hit is determined by
149 // the last part of the concatenated streamed tdc, which is 6 bits and
150 // therefore you mask the word1 with 63 (which is 111111 in binary) so as
151 // only to keep the tdc.
152
153 outHit[i].time_ = (word1 & 63);
154
155 // The 36 remaining here is an artefact of the mapping that the charges have
156 // to adcs; its not particularly meaningful except that it establishes that
157 // 0 adc corresponds to 0 charge. The .00625 value is a value which is
158 // conglomerate but relates to the number of PE's produced; it will change
159 // based on the number of shunts employed during a run.
160
161 outHit[i].amp_ =
162 shunt *
163 ((charge1 + charge2 + charge3 + charge4 + charge5 - 36) * .00625);
164 }
165
166 return;
167}
Definition objdef.h:49
Unsigned Arbitrary Precision Type.
Definition ap_int.h:166