1 #include <ot/liberty/lut.hpp>
2
3 namespace ot {
4
5 // Function: is_time_lut_var
is_time_lut_var(LutVar v)6 bool is_time_lut_var(LutVar v) {
7 switch(v) {
8 case LutVar::INPUT_NET_TRANSITION:
9 case LutVar::CONSTRAINED_PIN_TRANSITION:
10 case LutVar::RELATED_PIN_TRANSITION:
11 case LutVar::INPUT_TRANSITION_TIME:
12 return true;
13 break;
14
15 default:
16 return false;
17 break;
18 }
19 }
20
21 // Function: is_capacitance_lut_var
is_capacitance_lut_var(LutVar v)22 bool is_capacitance_lut_var(LutVar v) {
23 switch(v) {
24 case LutVar::TOTAL_OUTPUT_NET_CAPACITANCE:
25 return true;
26 break;
27
28 default:
29 return false;
30 break;
31 }
32 }
33
34 // Function: to_string
to_string(LutVar v)35 std::string to_string(LutVar v) {
36
37 switch(v) {
38 case LutVar::TOTAL_OUTPUT_NET_CAPACITANCE:
39 return "total_output_net_capacitance";
40 break;
41
42 case LutVar::INPUT_NET_TRANSITION:
43 return "input_net_transition";
44 break;
45
46 case LutVar::CONSTRAINED_PIN_TRANSITION:
47 return "constrained_pin_transition";
48 break;
49
50 case LutVar::RELATED_PIN_TRANSITION:
51 return "related_pin_transition";
52 break;
53
54 case LutVar::INPUT_TRANSITION_TIME:
55 return "input_transition_time";
56 break;
57
58 default:
59 return "undefined";
60 break;
61 }
62 }
63
64 // ------------------------------------------------------------------------------------------------
65
66 // Operator: <<
operator <<(std::ostream & os,const LutTemplate & lut)67 std::ostream& operator << (std::ostream& os, const LutTemplate& lut) {
68
69 // Write the lut template name.
70 os << "lu_table_template (" << lut.name << ") {\n";
71
72 // Write variables.
73 if(lut.variable1) {
74 os << " variable_1: " << to_string(*(lut.variable1)) << ";\n";
75 }
76
77 if(lut.variable2) {
78 os << " variable_2: " << to_string(*(lut.variable2)) << ";\n";
79 }
80
81 // Write indices.
82 if(!lut.indices1.empty()) {
83 os << " index_1 (\"";
84 for(size_t i=0; i<lut.indices1.size(); i++) {
85 if(i) {
86 os << ", ";
87 }
88 os << lut.indices1[i];
89 }
90 os << "\");\n";
91 }
92
93 if(!lut.indices2.empty()) {
94 os << " index_2 (\"";
95 for(size_t i=0; i<lut.indices2.size(); i++) {
96 if(i) {
97 os << ", ";
98 }
99 os << lut.indices2[i];
100 }
101 os << "\");\n";
102 }
103
104 // Write the lut template ending group symbol.
105 os <<"}\n";
106
107 return os;
108 }
109
110 // ------------------------------------------------------------------------------------------------
111
112 // Function: scale_time
scale_time(float s)113 void Lut::scale_time(float s) {
114
115 if(lut_template) {
116 if(auto v1 = lut_template->variable1; v1 && is_time_lut_var(*v1)) {
117 for(auto& v : indices1) {
118 v *= s;
119 }
120 }
121 if(auto v2 = lut_template->variable2; v2 && is_time_lut_var(*v2)) {
122 for(auto& v : indices2) {
123 v *= s;
124 }
125 }
126 }
127
128 // scale the table
129 for(auto& v : table) {
130 v *= s;
131 }
132 }
133
134 // Function: scale_capacitance
scale_capacitance(float s)135 void Lut::scale_capacitance(float s) {
136
137 if(lut_template) {
138 if(auto v1 = lut_template->variable1; v1 && is_capacitance_lut_var(*v1)) {
139 for(auto& v : indices1) {
140 v *= s;
141 }
142 }
143 if(auto v2 = lut_template->variable2; v2 && is_capacitance_lut_var(*v2)) {
144 for(auto& v : indices2) {
145 v *= s;
146 }
147 }
148 }
149 }
150
151 // Function: is_scalar
is_scalar() const152 bool Lut::is_scalar() const {
153 return indices1.size() == 1 && indices2.size() == 1;
154 }
155
156 // Function: empty
empty() const157 inline bool Lut::empty() const {
158 return indices1.size() == 0 && indices2.size() == 0;
159 }
160
161 // Function: lut
162 // Performs the linear inter/extra polation between a segment (x1, x2) which satisfies the
163 // function f(x1) = y1 and f(x2) = y2. There are five cases: 1) x < x1, 2) x = x1,
164 // 3) x1 < x < x2, 4) x = x2, and 5) x > x2. For cases 1) and 5), extra-polation is needed.
165 // Cases 2) and 4) are boundary cases. Case 3) requires the inter-polation.
operator ()(float val1,float val2) const166 float Lut::operator()(float val1, float val2) const {
167
168 if(indices1.size() < 1 || indices2.size() < 1) {
169 OT_LOGF("invalid lut indices size");
170 }
171
172 // Interpolation
173 constexpr auto interpolate = [] (float x, float x1, float x2, float y1, float y2) {
174
175 assert(x1 < x2);
176
177 if(x >= std::numeric_limits<float>::max() || x <= std::numeric_limits<float>::lowest()) {
178 return x;
179 }
180
181 float slope = (y2 - y1) / (x2 - x1);
182
183 if(x < x1) return y1 - (x1 - x) * slope; // Extrapolation.
184 else if(x > x2) return y2 + (x - x2) * slope; // Extrapolation.
185 else if(x == x1) return y1; // Boundary case.
186 else if(x == x2) return y2; // Boundary case.
187 else return y1 + (x - x1) * slope; // Interpolation.
188 };
189
190 // Case 1: scalar
191 if(is_scalar()) return table[0];
192
193 int idx1[2], idx2[2];
194
195 idx1[1] = std::lower_bound(indices1.begin(), indices1.end(), val1) - indices1.begin();
196 idx2[1] = std::lower_bound(indices2.begin(), indices2.end(), val2) - indices2.begin();
197
198 // Case 2: linear inter/extra polation.
199 idx1[1] = std::max(1, std::min(idx1[1], (int)(indices1.size() - 1)));
200 idx2[1] = std::max(1, std::min(idx2[1], (int)(indices2.size() - 1)));
201 idx1[0] = idx1[1] - 1;
202 idx2[0] = idx2[1] - 1;
203
204 //printf("Perform the linear interpolation on val1=%.5f (%d %d) and val2=%.5f (%d %d)\n",
205 // val1, idx1[0], idx1[1], val2, idx2[0], idx2[1]);
206
207 // 1xN array (N>=2)
208 if(indices1.size() == 1) {
209 return interpolate(
210 val2,
211 indices2[idx2[0]],
212 indices2[idx2[1]],
213 table[idx2[0]],
214 table[idx2[1]]
215 );
216 }
217 // Nx1 array (N>=2)
218 else if(indices2.size() == 1) {
219 return interpolate(
220 val1,
221 indices1[idx1[0]],
222 indices1[idx1[1]],
223 table[idx1[0]*indices2.size()],
224 table[idx1[1]*indices2.size()]
225 );
226 }
227 // NxN array (N>=2)
228 else {
229 float numeric[2];
230
231 numeric[0] = interpolate(
232 val1,
233 indices1[idx1[0]],
234 indices1[idx1[1]],
235 table[idx1[0]*indices2.size() + idx2[0]],
236 table[idx1[1]*indices2.size() + idx2[0]]
237 );
238
239 numeric[1] = interpolate(
240 val1,
241 indices1[idx1[0]],
242 indices1[idx1[1]],
243 table[idx1[0]*indices2.size() + idx2[1]],
244 table[idx1[1]*indices2.size() + idx2[1]]
245 );
246
247 return interpolate(val2, indices2[idx2[0]], indices2[idx2[1]], numeric[0], numeric[1]);
248 }
249 }
250
251 // operator
operator <<(std::ostream & os,const Lut & lut)252 std::ostream& operator << (std::ostream& os, const Lut& lut) {
253
254 // Write the indices1.
255 if(!lut.indices1.empty()) {
256 os << " index_1 (\"";
257 for(size_t i=0; i<lut.indices1.size(); ++i) {
258 if(i) {
259 os << ", ";
260 }
261 os << lut.indices1[i];
262 }
263 os << "\");\n";
264 }
265
266 // Write the indices2.
267 if(!lut.indices2.empty()) {
268 os << " index_2 (\"";
269 for(size_t i=0; i<lut.indices2.size(); ++i) {
270 if(i) {
271 os << ", ";
272 }
273 os << lut.indices2[i];
274 }
275 os << "\");\n";
276 }
277
278 // Write the values.
279 if(!lut.table.empty()) {
280 os << " values (\n";
281 for(size_t i=0; i<lut.indices1.size(); ++i) {
282 os << " \"";
283 for(size_t j=0; j<lut.indices2.size(); ++j) {
284 if(j) {
285 os << ", ";
286 }
287 os << lut.table[i*lut.indices2.size()+j];
288 }
289 os << "\",\n";
290 }
291 os << " );\n";
292 }
293
294 return os;
295 }
296
297
298
299
300 }; // end of namespace ot ------------------------------------------------------------------------
301