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