1 #include <ot/timer/timer.hpp>
2
3 namespace ot {
4
5 // Constructor
Point(const Pin & p,Tran t,float v)6 Point::Point(const Pin& p, Tran t, float v) :
7 pin {p},
8 transition {t},
9 at {v} {
10 }
11
12 // ------------------------------------------------------------------------------------------------
13
14 // Constructor
Path(float slk,const Endpoint * ept)15 Path::Path(float slk, const Endpoint* ept) :
16 slack {slk},
17 endpoint {ept} {
18 }
19
20 // Procedure: dump_tau18
dump_tau18(std::ostream & os) const21 void Path::dump_tau18(std::ostream& os) const{
22
23 std::regex replace(":");
24
25 auto el = endpoint->split();
26 auto rf = endpoint->transition();
27
28 os << "Endpoint: " << std::regex_replace(back().pin.name(), replace, "/") << '\n';
29 os << "Beginpoint: " << std::regex_replace(front().pin.name(), replace, "/") << '\n';
30 //os << "= Required Time " << '\n'; //TODO: ignore RAT for tau18 benchmark
31 float rat = 0.0;
32 if(endpoint->test() != nullptr){
33 rat = *(endpoint->test()->rat(el, rf));
34 }
35 else{
36 rat = *(endpoint->primary_output()->rat(el, rf));
37 }
38 auto beg_at = front().at;
39 auto end_at = back().at;
40 auto path_slack = el == MIN ? ((end_at - beg_at) - rat) : (rat - (end_at - beg_at));
41 os << "= Required Time " << rat << '\n';
42 //Arrival Time is the total delay
43 os << "- Arrival Time " << end_at - beg_at << '\n';
44 //os << "- Arrival Time " << back().at << '\n';
45 os << "= Slack Time " << path_slack << '\n';
46
47 float at_offset = front().at;
48 std::optional<float> pi_at;
49
50 for(const auto& p : *this) {
51
52 if(!pi_at){ os << "- "; }
53 else{ os << p.at-*pi_at << " "; }
54 os << p.at-at_offset << " ";
55
56 if(p.transition == RISE){ os << "^ "; }
57 else{ os << "v "; }
58
59 os << std::regex_replace(p.pin.name(), replace, "/") << '\n';
60 pi_at = p.at;
61 }
62 os << '\n';
63
64 }
65
66 // Procedure: dump
67 // dump the path in the following format:
68 //
69 // Startpoint : inp1
70 // Endpoint : f1:D
71 // Analysis type : early
72 // ------------------------------------------------------
73 // Type Delay Time Dir Description
74 // ------------------------------------------------------
75 // port 0.000 0.000 fall inp1
76 // pin 0.000 0.000 fall u1:A (NAND2X1)
77 // pin 2.786 2.786 rise u1:Y (NAND2X1)
78 // pin 0.000 2.786 rise u4:A (NOR2X1)
79 // pin 0.181 2.967 fall u4:Y (NOR2X1)
80 // pin 0.000 2.967 fall f1:D (DFFNEGX1)
81 // arrival 2.967 data arrival time
82 //
83 // clock 25.000 25.000 fall f1:CLK (DFFNEGX1)
84 // constraint 1.518 26.518 library hold_falling
85 // required 26.518 data required time
86 // ------------------------------------------------------
87 // slack -23.551 VIOLATED
88 //
dump(std::ostream & os) const89 void Path::dump(std::ostream& os) const {
90
91 if(empty()) {
92 os << "empty path\n";
93 return;
94 }
95
96 auto fmt = os.flags();
97 auto split = endpoint->split();
98 auto tran = endpoint->transition();
99 auto at = back().at;
100 auto rat = (split == MIN ? at - slack : at + slack);
101
102 // Print the head
103 os << "Startpoint : " << front().pin.name() << '\n';
104 os << "Endpoint : " << back().pin.name() << '\n';
105 os << "Analysis type : " << to_string(split) << '\n';
106
107 size_t w1 = 11;
108 size_t w2 = 12;
109 size_t w3 = 12;
110 size_t w4 = 6;
111 size_t w5 = 13;
112 size_t W = w1 + w2 + w3 + w4 + w5;
113
114 std::fill_n(std::ostream_iterator<char>(os), W, '-');
115 os << '\n'
116 << std::setw(w1) << "Type"
117 << std::setw(w2) << "Delay"
118 << std::setw(w3) << "Time"
119 << std::setw(w4) << "Dir";
120 std::fill_n(std::ostream_iterator<char>(os), 2, ' ');
121 os << "Description" << '\n';
122 std::fill_n(std::ostream_iterator<char>(os), W, '-');
123 os << '\n';
124
125 // trace
126 os << std::fixed << std::setprecision(3);
127 std::optional<float> pi_at;
128
129 for(const auto& p : *this) {
130
131 // type
132 if(p.pin.primary_input() || p.pin.primary_output()) {
133 os << std::setw(w1) << "port";
134 }
135 else {
136 os << std::setw(w1) << "pin";
137 }
138
139 // delay
140 os << std::setw(w2);
141 if(pi_at) os << p.at - *pi_at;
142 else os << p.at;
143
144 // arrival time
145 os << std::setw(w3) << p.at;
146
147 // transition
148 os << std::setw(w4) << to_string(p.transition);
149
150 // pin name
151 std::fill_n(std::ostream_iterator<char>(os), 2, ' ');
152 if(os << p.pin.name(); p.pin.gate()) {
153 os << " (" << p.pin.gate()->cell_name() << ')';
154 }
155 os << '\n';
156
157 // cursor
158 pi_at = p.at;
159 }
160
161 os << std::setw(w1) << "arrival"
162 << std::setw(w2+w3) << at;
163 std::fill_n(std::ostream_iterator<char>(os), w4 + 2, ' ');
164 os << "data arrival time" << '\n';
165
166 // Print the required arrival time
167 os << '\n';
168
169 // test type
170 std::visit(Functors{
171 [&] (Test* test) {
172
173 auto tv = (test->_arc.timing_view())[split];
174 auto sum = 0.0f;
175
176 // related pin latency
177 os << std::setw(w1) << "related pin";
178 if(auto c = test->_related_at[split][tran]; c) {
179 sum += *c;
180 os << std::setw(w2) << *c << std::setw(w3) << sum;
181 }
182 else {
183 os << std::setw(w2+w3) << "n/a";
184 }
185
186 if(tv && tv->is_rising_edge_triggered()) {
187 os << std::setw(w4) << "rise";
188 }
189 else if(tv && tv->is_falling_edge_triggered()){
190 os << std::setw(w4) << "fall";
191 }
192 else {
193 os << "n/a";
194 }
195
196 std::fill_n(std::ostream_iterator<char>(os), 2, ' ');
197 if(os << test->related_pin().name(); test->related_pin().gate()) {
198 os << " (" << test->related_pin().gate()->cell_name() << ')';
199 }
200 os << '\n';
201
202 // constraint value
203 os << std::setw(w1) << "constraint";
204 if(auto c = test->_constraint[split][tran]; c) {
205
206 switch(split) {
207 case MIN:
208 sum += *c;
209 os << std::setw(w2) << c.value() << std::setw(w3) << sum;
210 break;
211
212 case MAX:
213 sum -= *c;
214 os << std::setw(w2) << -c.value() << std::setw(w3) << sum;
215 break;
216 }
217
218 // timing type
219 if(tv && tv->type) {
220 std::fill_n(std::ostream_iterator<char>(os), w4+2, ' ');
221 os << "library " << to_string(tv->type.value()) << '\n';
222 }
223 else {
224 os << '\n';
225 }
226 }
227 else {
228 os << std::setw(w2) << "n/a" << '\n';
229 }
230
231 // cppr credit
232 if(auto c = test->_cppr_credit[split][tran]; c) {
233 os << std::setw(w1) << "cppr credit";
234 sum += *c;
235 os << std::setw(w2) << *c << std::setw(w3) << sum << '\n';
236 }
237
238 OT_LOGW_IF(
239 std::fabs(sum - rat) > 1.0f,
240 "unstable numerics in PBA and GBA rats: ", sum, " vs ", rat
241 );
242 },
243 [&] (PrimaryOutput* po) {
244 os << std::setw(w1) << "port";
245 if(auto v = po->rat(split, tran); v) {
246 os << std::setw(w2) << *v << std::setw(w3) << *v;
247 std::fill_n(std::ostream_iterator<char>(os), w4+2, ' ');
248 os << "output port delay" << '\n';
249 }
250 else {
251 os << std::setw(w2) << "n/a" << '\n';
252 }
253 }
254 }, endpoint->_handle);
255
256 os << std::setw(w1) << "required" << std::setw(w2+w3) << rat;
257 std::fill_n(std::ostream_iterator<char>(os), w4+2, ' ');
258 os << "data required time" << '\n';
259
260 // slack
261 std::fill_n(std::ostream_iterator<char>(os), W, '-');
262 os << '\n' << std::setw(w1) << "slack" << std::setw(w2+w3) << slack;
263 std::fill_n(std::ostream_iterator<char>(os), w4+2, ' ');
264 os << (slack < 0.0f ? "VIOLATED" : "MET") << '\n';
265
266 // restore the format
267 os.flags(fmt);
268 }
269
270 // Operator <<
operator <<(std::ostream & os,const Path & path)271 std::ostream& operator << (std::ostream& os, const Path& path) {
272 path.dump(os);
273 return os;
274 }
275
276 // ------------------------------------------------------------------------------------------------
277
278 // Functoin: _extract
279 // Extract the path in ascending order.
extract()280 std::vector<Path> PathHeap::extract() {
281 std::sort_heap(_paths.begin(), _paths.end(), _comp);
282 std::vector<Path> P;
283 P.reserve(_paths.size());
284 std::transform(_paths.begin(), _paths.end(), std::back_inserter(P), [] (auto& ptr) {
285 return std::move(*ptr);
286 });
287 _paths.clear();
288 return P;
289 }
290
291 // Procedure: push
push(std::unique_ptr<Path> path)292 void PathHeap::push(std::unique_ptr<Path> path) {
293 _paths.push_back(std::move(path));
294 std::push_heap(_paths.begin(), _paths.end(), _comp);
295 }
296
297 // Procedure: pop
pop()298 void PathHeap::pop() {
299 if(_paths.empty()) {
300 return;
301 }
302 std::pop_heap(_paths.begin(), _paths.end(), _comp);
303 _paths.pop_back();
304 }
305
306 // Function: top
top() const307 Path* PathHeap::top() const {
308 return _paths.empty() ? nullptr : _paths.front().get();
309 }
310
311 // Procedure: fit
fit(size_t K)312 void PathHeap::fit(size_t K) {
313 while(_paths.size() > K) {
314 pop();
315 }
316 }
317
318 // Procedure: heapify
heapify()319 void PathHeap::heapify() {
320 std::make_heap(_paths.begin(), _paths.end(), _comp);
321 }
322
323 // Procedure: merge_and_fit
merge_and_fit(PathHeap && rhs,size_t K)324 void PathHeap::merge_and_fit(PathHeap&& rhs, size_t K) {
325
326 if(_paths.capacity() < rhs._paths.capacity()) {
327 _paths.swap(rhs._paths);
328 }
329
330 std::sort_heap(_paths.begin(), _paths.end(), _comp);
331 std::sort_heap(rhs._paths.begin(), rhs._paths.end(), _comp);
332
333 auto mid = _paths.insert(
334 _paths.end(),
335 std::make_move_iterator(rhs._paths.begin()),
336 std::make_move_iterator(rhs._paths.end())
337 );
338
339 rhs._paths.clear();
340
341 std::inplace_merge(_paths.begin(), mid, _paths.end(), _comp);
342
343 if(_paths.size() > K) {
344 _paths.resize(K);
345 }
346
347 heapify();
348 }
349
350 // Function: dump
dump() const351 std::string PathHeap::dump() const {
352 std::ostringstream oss;
353 oss << "# Paths: " << _paths.size() << '\n';
354 for(size_t i=0; i<_paths.size(); ++i) {
355 oss << "slack[" << i << "]: " << _paths[i]->slack << '\n';
356 }
357 return oss.str();
358 }
359
360 // ------------------------------------------------------------------------------------------------
361
362 // Function: report_timing
363 // Report the top-k report_timing
report_timing(size_t K)364 std::vector<Path> Timer::report_timing(size_t K) {
365 std::scoped_lock lock(_mutex);
366 return _report_timing(_worst_endpoints(K), K);
367 }
368
369 // Function: report_timing
report_timing(size_t K,Split el)370 std::vector<Path> Timer::report_timing(size_t K, Split el) {
371 std::scoped_lock lock(_mutex);
372 return _report_timing(_worst_endpoints(K, el), K);
373 }
374
375 // Function: report_timing
report_timing(size_t K,Tran rf)376 std::vector<Path> Timer::report_timing(size_t K, Tran rf) {
377 std::scoped_lock lock(_mutex);
378 return _report_timing(_worst_endpoints(K, rf), K);
379 }
380
381 // Function: report_timing
report_timing(size_t K,Split el,Tran rf)382 std::vector<Path> Timer::report_timing(size_t K, Split el, Tran rf) {
383 std::scoped_lock lock(_mutex);
384 return _report_timing(_worst_endpoints(K, el, rf), K);
385 }
386
387 // TODO (Guannan)
388 // Function: report_timing
report_timing(PathGuide guide)389 std::vector<Path> Timer::report_timing(PathGuide guide) {
390 std::scoped_lock lock(_mutex);
391 auto epts = _worst_endpoints(guide);
392 return {};
393 }
394
395 // Function: _report_timing
396 // Report the top-k report_timing
_report_timing(std::vector<Endpoint * > && epts,size_t K)397 std::vector<Path> Timer::_report_timing(std::vector<Endpoint*>&& epts, size_t K) {
398
399 assert(epts.size() <= K);
400
401 // No need to report anything.
402 if(K == 0 || epts.empty()) {
403 return {};
404 }
405
406 // No need to generate prefix tree
407 if(K == 1) {
408 std::vector<Path> paths;
409 paths.emplace_back(epts[0]->slack(), epts[0]);
410 auto sfxt = _sfxt_cache(*epts[0]);
411
412 OT_LOGW_IF(
413 std::fabs(*sfxt.slack() - paths[0].slack) > 1.0f,
414 "unstable numerics in PBA and GBA slacks: ", *sfxt.slack(), " vs ", paths[0].slack
415 );
416
417 //assert(std::fabs(*sfxt.slack() - paths[0].slack) < 0.1f);
418 _recover_datapath(paths[0], sfxt);
419 return paths;
420 }
421
422 // Generate the prefix tree
423 PathHeap heap;
424
425 _taskflow.transform_reduce(epts.begin(), epts.end(), heap,
426 [&] (PathHeap l, PathHeap r) {
427 l.merge_and_fit(std::move(r), K);
428 return l;
429 },
430 [&] (PathHeap heap, Endpoint* ept) {
431 _spur(*ept, K, heap);
432 return heap;
433 },
434 [&] (Endpoint* ept) {
435 PathHeap heap;
436 _spur(*ept, K, heap);
437 return heap;
438 }
439 );
440
441 _executor.run(_taskflow).wait();
442 _taskflow.clear();
443
444 return heap.extract();
445 }
446
447 // Procedure: _recover_prefix
448 // Recover the worst path prefix at a given pin.
_recover_prefix(Path & path,const SfxtCache & sfxt,size_t idx) const449 void Timer::_recover_prefix(Path& path, const SfxtCache& sfxt, size_t idx) const {
450
451 auto el = sfxt._el;
452 auto [v, rf] = _decode_pin(idx);
453
454 assert(v->_at[el][rf]);
455
456 path.emplace_front(*v, rf, *v->_at[el][rf]);
457
458 if(auto arc = v->_at[el][rf]->pi_arc; arc) {
459 _recover_prefix(path, sfxt, _encode_pin(arc->_from, v->_at[el][rf]->pi_rf));
460 }
461 }
462
463 // Procedure: _recover_datapath
464 // Recover the worst data path from a given suffix tree.
_recover_datapath(Path & path,const SfxtCache & sfxt) const465 void Timer::_recover_datapath(Path& path, const SfxtCache& sfxt) const {
466
467 if(!sfxt.__tree[sfxt._S]) {
468 return;
469 }
470
471 auto u = *sfxt.__tree[sfxt._S];
472 auto [upin, urf] = _decode_pin(u);
473
474 // data path source
475 assert(upin->_at[sfxt._el][urf]);
476 path.emplace_back(*upin, urf, *upin->_at[sfxt._el][urf]);
477
478 // recursive
479 while(u != sfxt._T) {
480 assert(sfxt.__link[u]);
481 auto [arc, frf, trf] = _decode_arc(*sfxt.__link[u]);
482 u = *sfxt.__tree[u];
483 std::tie(upin, urf) = _decode_pin(u);
484 assert(path.back().transition == frf && urf == trf);
485 auto at = path.back().at + *arc->_delay[sfxt._el][frf][trf];
486 path.emplace_back(*upin, urf, at);
487 }
488 }
489
490 // Procedure: _recover_datapath
491 // recover the data path from a given prefix tree node w.r.t. a suffix tree
_recover_datapath(Path & path,const SfxtCache & sfxt,const PfxtNode * node,size_t v) const492 void Timer::_recover_datapath(
493 Path& path, const SfxtCache& sfxt, const PfxtNode* node, size_t v
494 ) const {
495
496 if(node == nullptr) {
497 return;
498 }
499
500 _recover_datapath(path, sfxt, node->parent, node->from);
501
502 auto u = node->to;
503 auto [upin, urf] = _decode_pin(u);
504
505 // data path source
506 if(node->from == sfxt._S) {
507 assert(upin->_at[sfxt._el][urf]);
508 path.emplace_back(*upin, urf, *upin->_at[sfxt._el][urf]);
509 }
510 // internal deviation
511 else {
512 assert(!path.empty());
513 auto at = path.back().at + *node->arc->_delay[sfxt._el][path.back().transition][urf];
514 path.emplace_back(*upin, urf, at);
515 }
516
517 while(u != v) {
518 assert(sfxt.__link[u]);
519 auto [arc, frf, trf] = _decode_arc(*sfxt.__link[u]);
520 u = *sfxt.__tree[u];
521 std::tie(upin, urf) = _decode_pin(u);
522 assert(path.back().transition == frf && urf == trf);
523 auto at = path.back().at + *arc->_delay[sfxt._el][frf][trf];
524 path.emplace_back(*upin, urf, at);
525 }
526
527 }
528
529 }; // end of namespace ot. -----------------------------------------------------------------------
530
531
532
533
534
535
536