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