1 #include <ot/timer/timer.hpp>
2 
3 namespace ot {
4 
5 // ------------------------------------------------------------------------------------------------
6 
7 // Function: set_num_threads
set_num_threads(unsigned n)8 Timer& Timer::set_num_threads(unsigned n) {
9   std::scoped_lock lock(_mutex);
10   unsigned w = (n == 0) ? 0 : n-1;
11   OT_LOGI("using ", n, " threads (", w, " worker)");
12   // TODO
13   //_taskflow.num_workers(w);
14   return *this;
15 }
16 
17 // Procedure: _add_to_lineage
_add_to_lineage(tf::Task task)18 void Timer::_add_to_lineage(tf::Task task) {
19   _lineage | [&] (auto& p) { p.precede(task); };
20   _lineage = task;
21 }
22 
23 // Function: _max_pin_name_size
_max_pin_name_size() const24 size_t Timer::_max_pin_name_size() const {
25   if(_pins.empty()) {
26     return 0;
27   }
28   else {
29     return std::max_element(_pins.begin(), _pins.end(),
30       [] (const auto& l, const auto& r) {
31         return l.second._name.size() < r.second._name.size();
32       }
33     )->second._name.size();
34   }
35 }
36 
37 // Function: _max_net_name_size
_max_net_name_size() const38 size_t Timer::_max_net_name_size() const {
39   if(_nets.empty()) {
40     return 0;
41   }
42   else {
43     return std::max_element(_nets.begin(), _nets.end(),
44       [] (const auto& l, const auto& r) {
45         return l.second._name.size() < r.second._name.size();
46       }
47     )->second._name.size();
48   }
49 }
50 
51 // Function: repower_gate
52 // Change the size or level of an existing gate, e.g., NAND2_X2 to NAND2_X3. The gate's
53 // logic function and topology is guaranteed to be the same, along with the currently-connected
54 // nets. However, the pin capacitances of the new cell type might be different.
repower_gate(std::string gate,std::string cell)55 Timer& Timer::repower_gate(std::string gate, std::string cell) {
56 
57   std::scoped_lock lock(_mutex);
58 
59   auto task = _taskflow.emplace([this, gate=std::move(gate), cell=std::move(cell)] () {
60     _repower_gate(gate, cell);
61   });
62 
63   _add_to_lineage(task);
64 
65   return *this;
66 }
67 
68 // Procedure: _repower_gate
_repower_gate(const std::string & gname,const std::string & cname)69 void Timer::_repower_gate(const std::string& gname, const std::string& cname) {
70 
71   OT_LOGE_RIF(!_celllib[MIN] || !_celllib[MAX], "celllib not found");
72 
73   // Insert the gate if it doesn't exist.
74   if(auto gitr = _gates.find(gname); gitr == _gates.end()) {
75     OT_LOGW("gate ", gname, " doesn't exist (insert instead)");
76     _insert_gate(gname, cname);
77     return;
78   }
79   else {
80 
81     auto cell = CellView {_celllib[MIN]->cell(cname), _celllib[MAX]->cell(cname)};
82 
83     OT_LOGE_RIF(!cell[MIN] || !cell[MAX], "cell ", cname, " not found");
84 
85     auto& gate = gitr->second;
86 
87     // Remap the cellpin
88     for(auto pin : gate._pins) {
89       FOR_EACH_EL(el) {
90         assert(pin->cellpin(el));
91         if(const auto cpin = cell[el]->cellpin(pin->cellpin(el)->name)) {
92           pin->_remap_cellpin(el, *cpin);
93         }
94         else {
95           OT_LOGE(
96             "repower ", gname, " with ", cname, " failed (cellpin mismatched)"
97           );
98         }
99       }
100     }
101 
102     gate._cell = cell;
103 
104     // reconstruct the timing and tests
105     _remove_gate_arcs(gate);
106     _insert_gate_arcs(gate);
107 
108     // Insert the gate to the frontier
109     for(auto pin : gate._pins) {
110       _insert_frontier(*pin);
111       for(auto arc : pin->_fanin) {
112         _insert_frontier(arc->_from);
113       }
114     }
115   }
116 }
117 
118 // Fucntion: insert_gate
119 // Create a new gate in the design. This newly-created gate is "not yet" connected to
120 // any other gates or wires. The gate to insert cannot conflict with existing gates.
insert_gate(std::string gate,std::string cell)121 Timer& Timer::insert_gate(std::string gate, std::string cell) {
122 
123   std::scoped_lock lock(_mutex);
124 
125   auto op = _taskflow.emplace([this, gate=std::move(gate), cell=std::move(cell)] () {
126     _insert_gate(gate, cell);
127   });
128 
129   _add_to_lineage(op);
130 
131   return *this;
132 }
133 
134 // Function: _insert_gate
_insert_gate(const std::string & gname,const std::string & cname)135 void Timer::_insert_gate(const std::string& gname, const std::string& cname) {
136 
137   OT_LOGE_RIF(!_celllib[MIN] || !_celllib[MAX], "celllib not found");
138 
139   if(_gates.find(gname) != _gates.end()) {
140     OT_LOGW("gate ", gname, " already existed");
141     return;
142   }
143 
144   auto cell = CellView {_celllib[MIN]->cell(cname), _celllib[MAX]->cell(cname)};
145 
146   if(!cell[MIN] || !cell[MAX]) {
147     OT_LOGE("cell ", cname, " not found in celllib");
148     return;
149   }
150 
151   auto& gate = _gates.try_emplace(gname, gname, cell).first->second;
152 
153   // Insert pins
154   for(const auto& [cpname, ecpin] : cell[MIN]->cellpins) {
155 
156     CellpinView cpv {&ecpin, cell[MAX]->cellpin(cpname)};
157 
158     if(!cpv[MIN] || !cpv[MAX]) {
159       OT_LOGF("cellpin ", cpname, " mismatched in celllib");
160     }
161 
162     auto& pin = _insert_pin(gname + ':' + cpname);
163     pin._handle = cpv;
164     pin._gate = &gate;
165 
166     gate._pins.push_back(&pin);
167   }
168 
169   _insert_gate_arcs(gate);
170 }
171 
172 // Fucntion: remove_gate
173 // Remove a gate from the current design. This is guaranteed to be called after the gate has
174 // been disconnected from the design using pin-level operations. The procedure iterates all
175 // pins in the cell to which the gate was attached. Each pin that is being iterated is either
176 // a cell input pin or cell output pin. In the former case, the pin might have constraint arc
177 // while in the later case, the ot_pin.has no output connections and all fanin edges should be
178 // removed here.
remove_gate(std::string gate)179 Timer& Timer::remove_gate(std::string gate) {
180 
181   std::scoped_lock lock(_mutex);
182 
183   auto op = _taskflow.emplace([this, gate=std::move(gate)] () {
184     if(auto gitr = _gates.find(gate); gitr != _gates.end()) {
185       _remove_gate(gitr->second);
186     }
187   });
188 
189   _add_to_lineage(op);
190 
191   return *this;
192 }
193 
194 // Procedure: _remove_gate
_remove_gate(Gate & gate)195 void Timer::_remove_gate(Gate& gate) {
196 
197   // Disconnect this gate from the design.
198   for(auto pin : gate._pins) {
199     _disconnect_pin(*pin);
200   }
201 
202   // Remove associated test
203   for(auto test : gate._tests) {
204     _remove_test(*test);
205   }
206 
207   // Remove associated arcs
208   for(auto arc : gate._arcs) {
209     _remove_arc(*arc);
210   }
211 
212   // Disconnect the gate and remove the pins from the gate
213   for(auto pin : gate._pins) {
214     _remove_pin(*pin);
215   }
216 
217   // remove the gate
218   _gates.erase(gate._name);
219 }
220 
221 // Procedure: _remove_gate_arcs
_remove_gate_arcs(Gate & gate)222 void Timer::_remove_gate_arcs(Gate& gate) {
223 
224   // remove associated tests
225   for(auto test : gate._tests) {
226     _remove_test(*test);
227   }
228   gate._tests.clear();
229 
230   // remove associated arcs
231   for(auto arc : gate._arcs) {
232     _remove_arc(*arc);
233   }
234   gate._arcs.clear();
235 }
236 
237 // Procedure: _insert_gate_arcs
_insert_gate_arcs(Gate & gate)238 void Timer::_insert_gate_arcs(Gate& gate) {
239 
240   assert(gate._tests.empty() && gate._arcs.empty());
241 
242   FOR_EACH_EL(el) {
243     for(const auto& [cpname, cp] : gate._cell[el]->cellpins) {
244       auto& to_pin = _insert_pin(gate._name + ':' + cpname);
245 
246       for(const auto& tm : cp.timings) {
247 
248         if(_is_redundant_timing(tm, el)) {
249           continue;
250         }
251 
252         TimingView tv{nullptr, nullptr};
253         tv[el] = &tm;
254 
255         auto& from_pin = _insert_pin(gate._name + ':' + tm.related_pin);
256         auto& arc = _insert_arc(from_pin, to_pin, tv);
257 
258         gate._arcs.push_back(&arc);
259         if(tm.is_constraint()) {
260           auto& test = _insert_test(arc);
261           gate._tests.push_back(&test);
262         }
263       }
264     }
265   }
266 }
267 
268 // Function: connect_pin
269 // Connect the pin to the corresponding net. The pin_name will either have the
270 // <gate name>:<cell pin name> syntax (e.g., u4:ZN) or be a primary input. The net name
271 // will match an existing net read in from a .spef file.
connect_pin(std::string pin,std::string net)272 Timer& Timer::connect_pin(std::string pin, std::string net) {
273 
274   std::scoped_lock lock(_mutex);
275 
276   auto op = _taskflow.emplace([this, pin=std::move(pin), net=std::move(net)] () {
277     auto p = _pins.find(pin);
278     auto n = _nets.find(net);
279     OT_LOGE_RIF(p==_pins.end() || n == _nets.end(),
280       "can't connect pin ", pin,  " to net ", net, " (pin/net not found)"
281     )
282     _connect_pin(p->second, n->second);
283   });
284 
285   _add_to_lineage(op);
286 
287   return *this;
288 }
289 
290 // Procedure: _connect_pin
_connect_pin(Pin & pin,Net & net)291 void Timer::_connect_pin(Pin& pin, Net& net) {
292 
293   // Connect the pin to the net and construct the edge connections.
294   net._insert_pin(pin);
295 
296   // Case 1: the pin is the root of the net.
297   if(&pin == net._root) {
298     for(auto leaf : net._pins) {
299       if(leaf != &pin) {
300         _insert_arc(pin, *leaf, net);
301       }
302     }
303   }
304   // Case 2: the pin is not a root of the net.
305   else {
306     if(net._root) {
307       _insert_arc(*net._root, pin, net);
308     }
309   }
310 
311   // TODO(twhuang) Enable the clock tree update?
312 }
313 
314 // Procedure: disconnect_pin
315 // Disconnect the pin from the net it is connected to. The pin_name will either have the
316 // <gate name>:<cell pin name> syntax (e.g., u4:ZN) or be a primary input.
disconnect_pin(std::string name)317 Timer& Timer::disconnect_pin(std::string name) {
318 
319   std::scoped_lock lock(_mutex);
320 
321   auto op = _taskflow.emplace([this, name=std::move(name)] () {
322     if(auto itr = _pins.find(name); itr != _pins.end()) {
323       _disconnect_pin(itr->second);
324     }
325   });
326 
327   _add_to_lineage(op);
328 
329   return *this;
330 }
331 
332 // Procedure: disconnect_pin
333 // TODO (twhuang)
334 // try get rid of find_fanin which can be wrong under multiple arcs.
_disconnect_pin(Pin & pin)335 void Timer::_disconnect_pin(Pin& pin) {
336 
337   auto net = pin._net;
338 
339   if(net == nullptr) return;
340 
341   // Case 1: the pin is a root of the net (i.e., root of the rctree)
342   if(&pin == net->_root) {
343     // Iterate the pinlist and delete the corresponding edge. Notice here we cannot iterate
344     // fanout of the node during removal.
345     for(auto leaf : net->_pins) {
346       if(leaf != net->_root) {
347         auto arc = leaf->_find_fanin(*net->_root);
348         assert(arc);
349         _remove_arc(*arc);
350       }
351     }
352   }
353   // Case 2: the pin is not a root of the net.
354   else {
355     if(net->_root) {
356       auto arc = pin._find_fanin(*net->_root);
357       assert(arc);
358       _remove_arc(*arc);
359     }
360   }
361 
362   // TODO: Enable the clock tree update.
363 
364   // Remove the pin from the net and enable the rc timing update.
365   net->_remove_pin(pin);
366 }
367 
368 // Function: insert_net
369 // Creates an empty net object with the input "net_name". By default, it will not be connected
370 // to any pins and have no parasitics (.spef). This net will be connected to existing pins in
371 // the design by the "connect_pin" and parasitics will be loaded by "spef".
insert_net(std::string name)372 Timer& Timer::insert_net(std::string name) {
373 
374   std::scoped_lock lock(_mutex);
375 
376   auto op = _taskflow.emplace([this, name=std::move(name)] () {
377     _insert_net(name);
378   });
379 
380   _add_to_lineage(op);
381 
382   return *this;
383 }
384 
385 // Function: _insert_net
_insert_net(const std::string & name)386 Net& Timer::_insert_net(const std::string& name) {
387   return _nets.try_emplace(name, name).first->second;
388 }
389 
390 // Procedure: remove_net
391 // Remove a net from the current design, which by default removes all associated pins.
remove_net(std::string name)392 Timer& Timer::remove_net(std::string name) {
393 
394   std::scoped_lock lock(_mutex);
395 
396   auto op = _taskflow.emplace([this, name=std::move(name)] () {
397     if(auto itr = _nets.find(name); itr != _nets.end()) {
398       _remove_net(itr->second);
399     }
400   });
401 
402   _add_to_lineage(op);
403 
404   return *this;
405 }
406 
407 // Function: _remove_net
_remove_net(Net & net)408 void Timer::_remove_net(Net& net) {
409 
410   if(net.num_pins() > 0) {
411     auto fetch = net._pins;
412     for(auto pin : fetch) {
413       _disconnect_pin(*pin);
414     }
415   }
416 
417   _nets.erase(net._name);
418 }
419 
420 // Function: _insert_pin
_insert_pin(const std::string & name)421 Pin& Timer::_insert_pin(const std::string& name) {
422 
423   // pin already exists
424   if(auto [itr, inserted] = _pins.try_emplace(name, name); !inserted) {
425     return itr->second;
426   }
427   // inserted a new pon
428   else {
429 
430     // Generate the pin idx
431     auto& pin = itr->second;
432 
433     // Assign the idx mapping
434     pin._idx = _pin_idx_gen.get();
435     resize_to_fit(pin._idx + 1, _idx2pin);
436     _idx2pin[pin._idx] = &pin;
437 
438     // insert to frontier
439     _insert_frontier(pin);
440 
441     return pin;
442   }
443 }
444 
445 // Function: _remove_pin
_remove_pin(Pin & pin)446 void Timer::_remove_pin(Pin& pin) {
447 
448   assert(pin.num_fanouts() == 0 && pin.num_fanins() == 0 && pin.net() == nullptr);
449 
450   _remove_frontier(pin);
451 
452   // remove the id mapping
453   _idx2pin[pin._idx] = nullptr;
454   _pin_idx_gen.recycle(pin._idx);
455 
456   // remove the pin
457   _pins.erase(pin._name);
458 }
459 
460 // Function: cppr
cppr(bool flag)461 Timer& Timer::cppr(bool flag) {
462 
463   std::scoped_lock lock(_mutex);
464 
465   auto op = _taskflow.emplace([this, flag] () {
466     _cppr(flag);
467   });
468 
469   _add_to_lineage(op);
470 
471   return *this;
472 }
473 
474 // Procedure: _cppr
475 // Enable/Disable common path pessimism removal (cppr) analysis
_cppr(bool enable)476 void Timer::_cppr(bool enable) {
477 
478   // nothing to do.
479   if((enable && _cppr_analysis) || (!enable && !_cppr_analysis)) {
480     return;
481   }
482 
483   if(enable) {
484     OT_LOGI("enable cppr analysis");
485     _cppr_analysis.emplace();
486   }
487   else {
488     OT_LOGI("disable cppr analysis");
489     _cppr_analysis.reset();
490   }
491 
492   for(auto& test : _tests) {
493     _insert_frontier(test._constrained_pin());
494   }
495 }
496 
497 // Function: clock
create_clock(std::string c,std::string s,float p)498 Timer& Timer::create_clock(std::string c, std::string s, float p) {
499 
500   std::scoped_lock lock(_mutex);
501 
502   auto op = _taskflow.emplace([this, c=std::move(c), s=std::move(s), p] () {
503     if(auto itr = _pins.find(s); itr != _pins.end()) {
504       _create_clock(c, itr->second, p);
505     }
506     else {
507       OT_LOGE("can't create clock ", c, " on source ", s, " (pin not found)");
508     }
509   });
510 
511   _add_to_lineage(op);
512 
513   return *this;
514 }
515 
516 // Function: create_clock
create_clock(std::string c,float p)517 Timer& Timer::create_clock(std::string c, float p) {
518 
519   std::scoped_lock lock(_mutex);
520 
521   auto op = _taskflow.emplace([this, c=std::move(c), p] () {
522     _create_clock(c, p);
523   });
524 
525   _add_to_lineage(op);
526 
527   return *this;
528 }
529 
530 // Procedure: _create_clock
_create_clock(const std::string & name,Pin & pin,float period)531 Clock& Timer::_create_clock(const std::string& name, Pin& pin, float period) {
532   auto& clock = _clocks.try_emplace(name, name, pin, period).first->second;
533   _insert_frontier(pin);
534   return clock;
535 }
536 
537 // Procedure: _create_clock
_create_clock(const std::string & name,float period)538 Clock& Timer::_create_clock(const std::string& name, float period) {
539   auto& clock = _clocks.try_emplace(name, name, period).first->second;
540   return clock;
541 }
542 
543 // Function: insert_primary_input
insert_primary_input(std::string name)544 Timer& Timer::insert_primary_input(std::string name) {
545 
546   std::scoped_lock lock(_mutex);
547 
548   auto op = _taskflow.emplace([this, name=std::move(name)] () {
549     _insert_primary_input(name);
550   });
551 
552   _add_to_lineage(op);
553 
554   return *this;
555 }
556 
557 // Procedure: _insert_primary_input
_insert_primary_input(const std::string & name)558 void Timer::_insert_primary_input(const std::string& name) {
559 
560   if(_pis.find(name) != _pis.end()) {
561     OT_LOGW("can't insert PI ", name, " (already existed)");
562     return;
563   }
564 
565   assert(_pins.find(name) == _pins.end());
566 
567   // Insert the pin and and pi
568   auto& pin = _insert_pin(name);
569   auto& pi = _pis.try_emplace(name, pin).first->second;
570 
571   // Associate the connection.
572   pin._handle = &pi;
573 
574   // Insert the pin to the frontier
575   _insert_frontier(pin);
576 
577   // Create a net for the po and connect the pin to the net.
578   auto& net = _insert_net(name);
579 
580   // Connect the pin to the net.
581   _connect_pin(pin, net);
582 }
583 
584 // Function: insert_primary_output
insert_primary_output(std::string name)585 Timer& Timer::insert_primary_output(std::string name) {
586 
587   std::scoped_lock lock(_mutex);
588 
589   auto op = _taskflow.emplace([this, name=std::move(name)] () {
590     _insert_primary_output(name);
591   });
592 
593   _add_to_lineage(op);
594 
595   return *this;
596 }
597 
598 // Procedure: _insert_primary_output
_insert_primary_output(const std::string & name)599 void Timer::_insert_primary_output(const std::string& name) {
600 
601   if(_pos.find(name) != _pos.end()) {
602     OT_LOGW("can't insert PO ", name, " (already existed)");
603     return;
604   }
605 
606   assert(_pins.find(name) == _pins.end());
607 
608   // Insert the pin and and pi
609   auto& pin = _insert_pin(name);
610   auto& po = _pos.try_emplace(name, pin).first->second;
611 
612   // Associate the connection.
613   pin._handle = &po;
614 
615   // Insert the pin to the frontier
616   _insert_frontier(pin);
617 
618   // Create a net for the po and connect the pin to the net.
619   auto& net = _insert_net(name);
620 
621   // Connect the pin to the net.
622   _connect_pin(pin, net);
623 }
624 
625 // Procedure: _insert_test
_insert_test(Arc & arc)626 Test& Timer::_insert_test(Arc& arc) {
627   auto& test = _tests.emplace_front(arc);
628   test._satellite = _tests.begin();
629   test._pin_satellite = arc._to._tests.insert(arc._to._tests.end(), &test);
630   return test;
631 }
632 
633 // Procedure: _remove_test
_remove_test(Test & test)634 void Timer::_remove_test(Test& test) {
635   assert(test._satellite);
636   if(test._pin_satellite) {
637     test._arc._to._tests.erase(*test._pin_satellite);
638   }
639   _tests.erase(*test._satellite);
640 }
641 
642 // Procedure: _remove_arc
643 // Remove an arc from the design. The procedure first disconnects the arc from its two ending
644 // pins, "from_pin" and "to_pin". Then it removes the arc from the design and insert both
645 // "from_pin" and "to_pin" into the pipeline.
_remove_arc(Arc & arc)646 void Timer::_remove_arc(Arc& arc) {
647 
648   assert(arc._satellite);
649 
650   arc._from._remove_fanout(arc);
651   arc._to._remove_fanin(arc);
652 
653   // Insert the two ends to the frontier list.
654   _insert_frontier(arc._from, arc._to);
655 
656   // remove the id mapping
657   _idx2arc[arc._idx] = nullptr;
658   _arc_idx_gen.recycle(arc._idx);
659 
660   // Remove this arc from the timer.
661   _arcs.erase(*arc._satellite);
662 }
663 
664 // Function: _insert_arc (net arc)
665 // Insert an net arc to the timer.
_insert_arc(Pin & from,Pin & to,Net & net)666 Arc& Timer::_insert_arc(Pin& from, Pin& to, Net& net) {
667 
668   OT_LOGF_IF(&from == &to, "net arc is a self loop at ", to._name);
669 
670   // Create a new arc
671   auto& arc = _arcs.emplace_front(from, to, net);
672   arc._satellite = _arcs.begin();
673 
674   from._insert_fanout(arc);
675   to._insert_fanin(arc);
676 
677   // Insert frontiers
678   _insert_frontier(from, to);
679 
680   // Assign the idx mapping
681   arc._idx = _arc_idx_gen.get();
682   resize_to_fit(arc._idx + 1, _idx2arc);
683   _idx2arc[arc._idx] = &arc;
684 
685   return arc;
686 }
687 
688 // Function: _insert_arc (cell arc)
689 // Insert a cell arc to the timing graph. A cell arc is a combinational link.
_insert_arc(Pin & from,Pin & to,TimingView tv)690 Arc& Timer::_insert_arc(Pin& from, Pin& to, TimingView tv) {
691 
692   //OT_LOGF_IF(&from == &to, "timing graph contains a self loop at ", to._name);
693 
694   // Create a new arc
695   auto& arc = _arcs.emplace_front(from, to, tv);
696   arc._satellite = _arcs.begin();
697   from._insert_fanout(arc);
698   to._insert_fanin(arc);
699 
700   // insert the arc into frontier list.
701   _insert_frontier(from, to);
702 
703   // Assign the idx mapping
704   arc._idx = _arc_idx_gen.get();
705   resize_to_fit(arc._idx + 1, _idx2arc);
706   _idx2arc[arc._idx] = &arc;
707 
708   return arc;
709 }
710 
711 // Procedure: _fprop_rc_timing
_fprop_rc_timing(Pin & pin)712 void Timer::_fprop_rc_timing(Pin& pin) {
713   if(auto net = pin._net; net) {
714     net->_update_rc_timing();
715   }
716 }
717 
718 // Procedure: _fprop_slew
_fprop_slew(Pin & pin)719 void Timer::_fprop_slew(Pin& pin) {
720 
721   // clear slew
722   pin._reset_slew();
723 
724   // PI
725   if(auto pi = pin.primary_input(); pi) {
726     FOR_EACH_EL_RF_IF(el, rf, pi->_slew[el][rf]) {
727       pin._relax_slew(nullptr, el, rf, el, rf, *(pi->_slew[el][rf]));
728     }
729   }
730 
731   // Relax the slew from its fanin.
732   for(auto arc : pin._fanin) {
733     arc->_fprop_slew();
734   }
735 }
736 
737 // Procedure: _fprop_delay
_fprop_delay(Pin & pin)738 void Timer::_fprop_delay(Pin& pin) {
739 
740   // clear delay
741   for(auto arc : pin._fanin) {
742     arc->_reset_delay();
743   }
744 
745   // Compute the delay from its fanin.
746   for(auto arc : pin._fanin) {
747     arc->_fprop_delay();
748   }
749 }
750 
751 // Procedure: _fprop_at
_fprop_at(Pin & pin)752 void Timer::_fprop_at(Pin& pin) {
753 
754   // clear at
755   pin._reset_at();
756 
757   // PI
758   if(auto pi = pin.primary_input(); pi) {
759     FOR_EACH_EL_RF_IF(el, rf, pi->_at[el][rf]) {
760       pin._relax_at(nullptr, el, rf, el, rf, *(pi->_at[el][rf]));
761     }
762   }
763 
764   // Relax the at from its fanin.
765   for(auto arc : pin._fanin) {
766     arc->_fprop_at();
767   }
768 }
769 
770 // Procedure: _fprop_test
_fprop_test(Pin & pin)771 void Timer::_fprop_test(Pin& pin) {
772 
773   // reset tests
774   for(auto test : pin._tests) {
775     test->_reset();
776   }
777 
778   // Obtain the rat
779   if(!_clocks.empty()) {
780 
781     // Update the rat
782     for(auto test : pin._tests) {
783       // TODO: currently we assume a single clock...
784       test->_fprop_rat(_clocks.begin()->second._period);
785 
786       // compute the cppr credit if any
787       if(_cppr_analysis) {
788         FOR_EACH_EL_RF_IF(el, rf, test->raw_slack(el, rf)) {
789           test->_cppr_credit[el][rf] = _cppr_credit(*test, el, rf);
790         }
791       }
792     }
793   }
794 }
795 
796 // Procedure: _bprop_rat
_bprop_rat(Pin & pin)797 void Timer::_bprop_rat(Pin& pin) {
798 
799   pin._reset_rat();
800 
801   // PO
802   if(auto po = pin.primary_output(); po) {
803     FOR_EACH_EL_RF_IF(el, rf, po->_rat[el][rf]) {
804       pin._relax_rat(nullptr, el, rf, el, rf, *(po->_rat[el][rf]));
805     }
806   }
807 
808   // Test
809   for(auto test : pin._tests) {
810     FOR_EACH_EL_RF_IF(el, rf, test->_rat[el][rf]) {
811       if(test->_cppr_credit[el][rf]) {
812         pin._relax_rat(
813           &test->_arc, el, rf, el, rf, *test->_rat[el][rf] + *test->_cppr_credit[el][rf]
814         );
815       }
816       else {
817         pin._relax_rat(&test->_arc, el, rf, el, rf, *test->_rat[el][rf]);
818       }
819     }
820   }
821 
822   // Relax the rat from its fanout.
823   for(auto arc : pin._fanout) {
824     arc->_bprop_rat();
825   }
826 }
827 
828 // Procedure: _build_fprop_cands
829 // Performs DFS to find all nodes in the fanout cone of frontiers.
_build_fprop_cands(Pin & from)830 void Timer::_build_fprop_cands(Pin& from) {
831 
832   assert(!from._has_state(Pin::FPROP_CAND) && !from._has_state(Pin::IN_FPROP_STACK));
833 
834   from._insert_state(Pin::FPROP_CAND | Pin::IN_FPROP_STACK);
835 
836   for(auto arc : from._fanout) {
837     if(auto& to = arc->_to; !to._has_state(Pin::FPROP_CAND)) {
838       _build_fprop_cands(to);
839     }
840     else if(to._has_state(Pin::IN_FPROP_STACK)) {
841       _scc_analysis = true;
842     }
843   }
844 
845   _fprop_cands.push_front(&from);  // insert from front for scc traversal
846   from._remove_state(Pin::IN_FPROP_STACK);
847 }
848 
849 // Procedure: _build_bprop_cands
850 // Perform the DFS to find all nodes in the fanin cone of fprop candidates.
_build_bprop_cands(Pin & to)851 void Timer::_build_bprop_cands(Pin& to) {
852 
853   assert(!to._has_state(Pin::BPROP_CAND) && !to._has_state(Pin::IN_BPROP_STACK));
854 
855   to._insert_state(Pin::BPROP_CAND | Pin::IN_BPROP_STACK);
856 
857   // add pin to scc
858   if(_scc_analysis && to._has_state(Pin::FPROP_CAND) && !to._scc) {
859     _scc_cands.push_back(&to);
860   }
861 
862   for(auto arc : to._fanin) {
863     if(auto& from=arc->_from; !from._has_state(Pin::BPROP_CAND)) {
864       _build_bprop_cands(from);
865     }
866   }
867 
868   _bprop_cands.push_front(&to);
869   to._remove_state(Pin::IN_BPROP_STACK);
870 }
871 
872 // Procedure: _build_prop_cands
_build_prop_cands()873 void Timer::_build_prop_cands() {
874 
875   _scc_analysis = false;
876 
877   // Discover all fprop candidates.
878   for(const auto& ftr : _frontiers) {
879     if(ftr->_has_state(Pin::FPROP_CAND)) {
880       continue;
881     }
882     _build_fprop_cands(*ftr);
883   }
884 
885   // Discover all bprop candidates.
886   for(auto fcand : _fprop_cands) {
887 
888     if(fcand->_has_state(Pin::BPROP_CAND)) {
889       continue;
890     }
891 
892     _scc_cands.clear();
893     _build_bprop_cands(*fcand);
894 
895     if(!_scc_analysis) {
896       assert(_scc_cands.empty());
897     }
898 
899     // here dfs returns with exacly one scc if exists
900     if(auto& c = _scc_cands; c.size() >= 2 || (c.size() == 1 && c[0]->has_self_loop())) {
901       auto& scc = _insert_scc(c);
902       scc._unloop();
903     }
904   }
905 }
906 
907 // Procedure: _build_prop_tasks
_build_prop_tasks()908 void Timer::_build_prop_tasks() {
909 
910   // explore propagation candidates
911   _build_prop_cands();
912 
913   // Emplace the fprop task
914   // (1) propagate the rc timing
915   // (2) propagate the slew
916   // (3) propagate the delay
917   // (4) propagate the arrival time.
918   for(auto pin : _fprop_cands) {
919     assert(!pin->_ftask);
920     pin->_ftask = _taskflow.emplace([this, pin] () {
921       _fprop_rc_timing(*pin);
922       _fprop_slew(*pin);
923       _fprop_delay(*pin);
924       _fprop_at(*pin);
925       _fprop_test(*pin);
926     });
927   }
928 
929   // Build the dependency
930   for(auto to : _fprop_cands) {
931     for(auto arc : to->_fanin) {
932       if(arc->_has_state(Arc::LOOP_BREAKER)) {
933         continue;
934       }
935       if(auto& from = arc->_from; from._has_state(Pin::FPROP_CAND)) {
936         from._ftask->precede(to->_ftask.value());
937       }
938     }
939   }
940 
941   // Emplace the bprop task
942   // (1) propagate the required arrival time
943   for(auto pin : _bprop_cands) {
944     assert(!pin->_btask);
945     pin->_btask = _taskflow.emplace([this, pin] () {
946       _bprop_rat(*pin);
947     });
948   }
949 
950   // Build the task dependencies.
951   for(auto to : _bprop_cands) {
952     for(auto arc : to->_fanin) {
953       if(arc->_has_state(Arc::LOOP_BREAKER)) {
954         continue;
955       }
956       if(auto& from = arc->_from; from._has_state(Pin::BPROP_CAND)) {
957         to->_btask->precede(from._btask.value());
958       }
959     }
960   }
961 
962   // Connect with ftasks
963   for(auto pin : _bprop_cands) {
964     if(pin->_btask->num_dependents() == 0 && pin->_ftask) {
965       pin->_ftask->precede(pin->_btask.value());
966     }
967   }
968 
969 }
970 
971 // Procedure: _clear_prop_tasks
_clear_prop_tasks()972 void Timer::_clear_prop_tasks() {
973 
974   // fprop is a subset of bprop
975   for(auto pin : _bprop_cands) {
976     pin->_ftask.reset();
977     pin->_btask.reset();
978     pin->_remove_state();
979   }
980 
981   _fprop_cands.clear();
982   _bprop_cands.clear();
983 }
984 
985 // Function: update_timing
986 // Perform comprehensive timing update:
987 // (1) grpah-based timing (GBA)
988 // (2) path-based timing (PBA)
update_timing()989 void Timer::update_timing() {
990   std::scoped_lock lock(_mutex);
991   _update_timing();
992 }
993 
994 // Function: _update_timing
_update_timing()995 void Timer::_update_timing() {
996 
997   // Timing is update-to-date
998   if(!_lineage) {
999     assert(_frontiers.size() == 0);
1000     return;
1001   }
1002 
1003   // materialize the lineage
1004   _executor.run(_taskflow).wait();
1005   _taskflow.clear();
1006   _lineage.reset();
1007 
1008   // Check if full update is required
1009   if(_has_state(FULL_TIMING)) {
1010     _insert_full_timing_frontiers();
1011   }
1012 
1013   // build propagation tasks
1014   _build_prop_tasks();
1015 
1016   // debug the graph
1017   //_taskflow.dump(std::cout);
1018 
1019   // Execute the task
1020   _executor.run(_taskflow).wait();
1021   _taskflow.clear();
1022 
1023   // Clear the propagation tasks.
1024   _clear_prop_tasks();
1025 
1026   // Clear frontiers
1027   _clear_frontiers();
1028 
1029   // clear the state
1030   _remove_state();
1031 }
1032 
1033 // Procedure: _update_area
_update_area()1034 void Timer::_update_area() {
1035 
1036   _update_timing();
1037 
1038   if(_has_state(AREA_UPDATED)) {
1039     return;
1040   }
1041 
1042   _area = 0.0f;
1043 
1044   for(const auto& kvp : _gates) {
1045     if(const auto& c = kvp.second._cell[MIN]; c->area) {
1046       _area = *_area + *c->area;
1047     }
1048     else {
1049       OT_LOGE("cell ", c->name, " has no area defined");
1050       _area.reset();
1051       break;
1052     }
1053   }
1054 
1055   _insert_state(AREA_UPDATED);
1056 }
1057 
1058 // Procedure: _update_power
_update_power()1059 void Timer::_update_power() {
1060 
1061   _update_timing();
1062 
1063   if(_has_state(POWER_UPDATED)) {
1064     return;
1065   }
1066 
1067   // Update the static leakage power
1068   _leakage_power = 0.0f;
1069 
1070   for(const auto& kvp : _gates) {
1071     if(const auto& c = kvp.second._cell[MIN]; c->leakage_power) {
1072       _leakage_power = *_leakage_power + *c->leakage_power;
1073     }
1074     else {
1075       OT_LOGE("cell ", c->name, " has no leakage_power defined");
1076       _leakage_power.reset();
1077       break;
1078     }
1079   }
1080 
1081   _insert_state(POWER_UPDATED);
1082 }
1083 
1084 // Procedure: _update_endpoints
_update_endpoints()1085 void Timer::_update_endpoints() {
1086 
1087   _update_timing();
1088 
1089   if(_has_state(EPTS_UPDATED)) {
1090     return;
1091   }
1092 
1093   // reset the storage and build task
1094   FOR_EACH_EL_RF(el, rf) {
1095 
1096     _endpoints[el][rf].clear();
1097 
1098     _taskflow.emplace([this, el=el, rf=rf] () {
1099 
1100       // for each po
1101       for(auto& po : _pos) {
1102         if(po.second.slack(el, rf).has_value()) {
1103           _endpoints[el][rf].emplace_back(el, rf, po.second);
1104         }
1105       }
1106 
1107       // for each test
1108       for(auto& test : _tests) {
1109         if(test.slack(el, rf).has_value()) {
1110           _endpoints[el][rf].emplace_back(el, rf, test);
1111         }
1112       }
1113 
1114       // sort endpoints
1115       std::sort(_endpoints[el][rf].begin(), _endpoints[el][rf].end());
1116 
1117       // update the worst negative slack (wns)
1118       if(!_endpoints[el][rf].empty()) {
1119         _wns[el][rf] = _endpoints[el][rf].front().slack();
1120       }
1121       else {
1122         _wns[el][rf] = std::nullopt;
1123       }
1124 
1125       // update the tns, and fep
1126       if(!_endpoints[el][rf].empty()) {
1127         _tns[el][rf] = 0.0f;
1128         _fep[el][rf] = 0;
1129         for(const auto& ept : _endpoints[el][rf]) {
1130           if(auto slack = ept.slack(); slack < 0.0f) {
1131             _tns[el][rf] = *_tns[el][rf] + slack;
1132             _fep[el][rf] = *_fep[el][rf] + 1;
1133           }
1134         }
1135       }
1136       else {
1137         _tns[el][rf] = std::nullopt;
1138         _fep[el][rf] = std::nullopt;
1139       }
1140     });
1141   }
1142 
1143   // run tasks
1144   _executor.run(_taskflow).wait();
1145   _taskflow.clear();
1146 
1147   _insert_state(EPTS_UPDATED);
1148 }
1149 
1150 // Function: tns
1151 // Update the total negative slack for any transition and timing split. The procedure applies
1152 // the parallel reduction to compute the value.
report_tns(std::optional<Split> el,std::optional<Tran> rf)1153 std::optional<float> Timer::report_tns(std::optional<Split> el, std::optional<Tran> rf) {
1154 
1155   std::scoped_lock lock(_mutex);
1156 
1157   _update_endpoints();
1158 
1159   std::optional<float> v;
1160 
1161   if(!el && !rf) {
1162     FOR_EACH_EL_RF_IF(s, t, _tns[s][t]) {
1163       v = !v ? _tns[s][t] : *v + *(_tns[s][t]);
1164     }
1165   }
1166   else if(el && !rf) {
1167     FOR_EACH_RF_IF(t, _tns[*el][t]) {
1168       v = !v ? _tns[*el][t] : *v + *(_tns[*el][t]);
1169     }
1170   }
1171   else if(!el && rf) {
1172     FOR_EACH_EL_IF(s, _tns[s][*rf]) {
1173       v = !v ? _tns[s][*rf] : *v + *(_tns[s][*rf]);
1174     }
1175   }
1176   else {
1177     v = _tns[*el][*rf];
1178   }
1179 
1180   return v;
1181 }
1182 
1183 // Function: wns
1184 // Update the total negative slack for any transition and timing split. The procedure apply
1185 // the parallel reduction to compute the value.
report_wns(std::optional<Split> el,std::optional<Tran> rf)1186 std::optional<float> Timer::report_wns(std::optional<Split> el, std::optional<Tran> rf) {
1187 
1188   std::scoped_lock lock(_mutex);
1189 
1190   _update_endpoints();
1191 
1192   std::optional<float> v;
1193 
1194   if(!el && !rf) {
1195     FOR_EACH_EL_RF_IF(s, t, _wns[s][t]) {
1196       v = !v ? _wns[s][t] : std::min(*v, *(_wns[s][t]));
1197     }
1198   }
1199   else if(el && !rf) {
1200     FOR_EACH_RF_IF(t, _wns[*el][t]) {
1201       v = !v ? _wns[*el][t] : std::min(*v, *(_wns[*el][t]));
1202     }
1203   }
1204   else if(!el && rf) {
1205     FOR_EACH_EL_IF(s, _wns[s][*rf]) {
1206       v = !v ? _wns[s][*rf] : std::min(*v, *(_wns[s][*rf]));
1207     }
1208   }
1209   else {
1210     v = _wns[*el][*rf];
1211   }
1212 
1213   return v;
1214 }
1215 
1216 // Function: fep
1217 // Update the failing end points
report_fep(std::optional<Split> el,std::optional<Tran> rf)1218 std::optional<size_t> Timer::report_fep(std::optional<Split> el, std::optional<Tran> rf) {
1219 
1220   std::scoped_lock lock(_mutex);
1221 
1222   _update_endpoints();
1223 
1224   std::optional<size_t> v;
1225 
1226   if(!el && !rf) {
1227     FOR_EACH_EL_RF_IF(s, t, _fep[s][t]) {
1228       v = !v ? _fep[s][t] : *v + *(_fep[s][t]);
1229     }
1230   }
1231   else if(el && !rf) {
1232     FOR_EACH_RF_IF(t, _fep[*el][t]) {
1233       v = !v ? _fep[*el][t] : *v + *(_fep[*el][t]);
1234     }
1235   }
1236   else if(!el && rf) {
1237     FOR_EACH_EL_IF(s, _fep[s][*rf]) {
1238       v = !v ? _fep[s][*rf] : *v + *(_fep[s][*rf]);
1239     }
1240   }
1241   else {
1242     v = _fep[*el][*rf];
1243   }
1244 
1245   return v;
1246 }
1247 
1248 // Function: leakage_power
report_leakage_power()1249 std::optional<float> Timer::report_leakage_power() {
1250   std::scoped_lock lock(_mutex);
1251   _update_power();
1252   return _leakage_power;
1253 }
1254 
1255 // Function: area
1256 // Sum up the area of each gate in the design.
report_area()1257 std::optional<float> Timer::report_area() {
1258   std::scoped_lock lock(_mutex);
1259   _update_area();
1260   return _area;
1261 }
1262 
1263 // Procedure: _enable_full_timing_update
_enable_full_timing_update()1264 void Timer::_enable_full_timing_update() {
1265   _insert_state(FULL_TIMING);
1266 }
1267 
1268 // Procedure: _insert_full_timing_frontiers
_insert_full_timing_frontiers()1269 void Timer::_insert_full_timing_frontiers() {
1270 
1271   // insert all zero-fanin pins to the frontier list
1272   for(auto& kvp : _pins) {
1273     _insert_frontier(kvp.second);
1274   }
1275 
1276   // clear the rc-net update flag
1277   for(auto& kvp : _nets) {
1278     kvp.second._rc_timing_updated = false;
1279   }
1280 }
1281 
1282 // Procedure: _insert_frontier
_insert_frontier(Pin & pin)1283 void Timer::_insert_frontier(Pin& pin) {
1284 
1285   if(pin._frontier_satellite) {
1286     return;
1287   }
1288 
1289   pin._frontier_satellite = _frontiers.insert(_frontiers.end(), &pin);
1290 
1291   // reset the scc.
1292   if(pin._scc) {
1293     _remove_scc(*pin._scc);
1294   }
1295 }
1296 
1297 // Procedure: _remove_frontier
_remove_frontier(Pin & pin)1298 void Timer::_remove_frontier(Pin& pin) {
1299   if(pin._frontier_satellite) {
1300     _frontiers.erase(*pin._frontier_satellite);
1301     pin._frontier_satellite.reset();
1302   }
1303 }
1304 
1305 // Procedure: _clear_frontiers
_clear_frontiers()1306 void Timer::_clear_frontiers() {
1307   for(auto& ftr : _frontiers) {
1308     ftr->_frontier_satellite.reset();
1309   }
1310   _frontiers.clear();
1311 }
1312 
1313 // Procedure: _insert_scc
_insert_scc(std::vector<Pin * > & cands)1314 SCC& Timer::_insert_scc(std::vector<Pin*>& cands) {
1315 
1316   // create scc only of size at least two
1317   auto& scc = _sccs.emplace_front(std::move(cands));
1318   scc._satellite = _sccs.begin();
1319 
1320   return scc;
1321 }
1322 
1323 // Procedure: _remove_scc
_remove_scc(SCC & scc)1324 void Timer::_remove_scc(SCC& scc) {
1325   assert(scc._satellite);
1326   scc._clear();
1327   _sccs.erase(*scc._satellite);
1328 }
1329 
1330 // Function: report_at
1331 // Report the arrival time in picoseconds at a given pin name.
report_at(const std::string & name,Split m,Tran t)1332 std::optional<float> Timer::report_at(const std::string& name, Split m, Tran t) {
1333   std::scoped_lock lock(_mutex);
1334   return _report_at(name, m, t);
1335 }
1336 
1337 // Function: _report_at
_report_at(const std::string & name,Split m,Tran t)1338 std::optional<float> Timer::_report_at(const std::string& name, Split m, Tran t) {
1339   _update_timing();
1340   if(auto itr = _pins.find(name); itr != _pins.end() && itr->second._at[m][t]) {
1341     return itr->second._at[m][t]->numeric;
1342   }
1343   else return std::nullopt;
1344 }
1345 
1346 // Function: report_rat
1347 // Report the required arrival time in picoseconds at a given pin name.
report_rat(const std::string & name,Split m,Tran t)1348 std::optional<float> Timer::report_rat(const std::string& name, Split m, Tran t) {
1349   std::scoped_lock lock(_mutex);
1350   return _report_rat(name, m, t);
1351 }
1352 
1353 // Function: _report_rat
_report_rat(const std::string & name,Split m,Tran t)1354 std::optional<float> Timer::_report_rat(const std::string& name, Split m, Tran t) {
1355   _update_timing();
1356   if(auto itr = _pins.find(name); itr != _pins.end() && itr->second._at[m][t]) {
1357     return itr->second._rat[m][t];
1358   }
1359   else return std::nullopt;
1360 }
1361 
1362 // Function: report_slew
1363 // Report the slew in picoseconds at a given pin name.
report_slew(const std::string & name,Split m,Tran t)1364 std::optional<float> Timer::report_slew(const std::string& name, Split m, Tran t) {
1365   std::scoped_lock lock(_mutex);
1366   return _report_slew(name, m, t);
1367 }
1368 
1369 // Function: _report_slew
_report_slew(const std::string & name,Split m,Tran t)1370 std::optional<float> Timer::_report_slew(const std::string& name, Split m, Tran t) {
1371   _update_timing();
1372   if(auto itr = _pins.find(name); itr != _pins.end() && itr->second._slew[m][t]) {
1373     return itr->second._slew[m][t]->numeric;
1374   }
1375   else return std::nullopt;
1376 }
1377 
1378 // Function: report_slack
report_slack(const std::string & pin,Split m,Tran t)1379 std::optional<float> Timer::report_slack(const std::string& pin, Split m, Tran t) {
1380   std::scoped_lock lock(_mutex);
1381   return _report_slack(pin, m, t);
1382 }
1383 
1384 // Function: _report_slack
_report_slack(const std::string & pin,Split m,Tran t)1385 std::optional<float> Timer::_report_slack(const std::string& pin, Split m, Tran t) {
1386   _update_timing();
1387   if(auto itr = _pins.find(pin); itr != _pins.end()) {
1388     return itr->second.slack(m, t);
1389   }
1390   else return std::nullopt;
1391 }
1392 
1393 // Function: report_load
1394 // Report the load at a given pin name
report_load(const std::string & name,Split m,Tran t)1395 std::optional<float> Timer::report_load(const std::string& name, Split m, Tran t) {
1396   std::scoped_lock lock(_mutex);
1397   return _report_load(name, m, t);
1398 }
1399 
1400 // Function: _report_load
_report_load(const std::string & name,Split m,Tran t)1401 std::optional<float> Timer::_report_load(const std::string& name, Split m, Tran t) {
1402   _update_timing();
1403   if(auto itr = _nets.find(name); itr != _nets.end()) {
1404     return itr->second._load(m, t);
1405   }
1406   else return std::nullopt;
1407 }
1408 
1409 // Function: set_at
set_at(std::string name,Split m,Tran t,std::optional<float> v)1410 Timer& Timer::set_at(std::string name, Split m, Tran t, std::optional<float> v) {
1411 
1412   std::scoped_lock lock(_mutex);
1413 
1414   auto task = _taskflow.emplace([this, name=std::move(name), m, t, v] () {
1415     if(auto itr = _pis.find(name); itr != _pis.end()) {
1416       _set_at(itr->second, m, t, v);
1417     }
1418     else {
1419       OT_LOGE("can't set at (PI ", name, " not found)");
1420     }
1421   });
1422 
1423   _add_to_lineage(task);
1424 
1425   return *this;
1426 }
1427 
1428 // Procedure: _set_at
_set_at(PrimaryInput & pi,Split m,Tran t,std::optional<float> v)1429 void Timer::_set_at(PrimaryInput& pi, Split m, Tran t, std::optional<float> v) {
1430   pi._at[m][t] = v;
1431   _insert_frontier(pi._pin);
1432 }
1433 
1434 // Function: set_rat
set_rat(std::string name,Split m,Tran t,std::optional<float> v)1435 Timer& Timer::set_rat(std::string name, Split m, Tran t, std::optional<float> v) {
1436 
1437   std::scoped_lock lock(_mutex);
1438 
1439   auto op = _taskflow.emplace([this, name=std::move(name), m, t, v] () {
1440     if(auto itr = _pos.find(name); itr != _pos.end()) {
1441       _set_rat(itr->second, m, t, v);
1442     }
1443     else {
1444       OT_LOGE("can't set rat (PO ", name, " not found)");
1445     }
1446   });
1447 
1448   _add_to_lineage(op);
1449 
1450   return *this;
1451 }
1452 
1453 // Procedure: _set_rat
_set_rat(PrimaryOutput & po,Split m,Tran t,std::optional<float> v)1454 void Timer::_set_rat(PrimaryOutput& po, Split m, Tran t, std::optional<float> v) {
1455   po._rat[m][t] = v;
1456   _insert_frontier(po._pin);
1457 }
1458 
1459 // Function: set_slew
set_slew(std::string name,Split m,Tran t,std::optional<float> v)1460 Timer& Timer::set_slew(std::string name, Split m, Tran t, std::optional<float> v) {
1461 
1462   std::scoped_lock lock(_mutex);
1463 
1464   auto task = _taskflow.emplace([this, name=std::move(name), m, t, v] () {
1465     if(auto itr = _pis.find(name); itr != _pis.end()) {
1466       _set_slew(itr->second, m, t, v);
1467     }
1468     else {
1469       OT_LOGE("can't set slew (PI ", name, " not found)");
1470     }
1471   });
1472 
1473   _add_to_lineage(task);
1474 
1475   return *this;
1476 }
1477 
1478 // Procedure: _set_slew
_set_slew(PrimaryInput & pi,Split m,Tran t,std::optional<float> v)1479 void Timer::_set_slew(PrimaryInput& pi, Split m, Tran t, std::optional<float> v) {
1480   pi._slew[m][t] = v;
1481   _insert_frontier(pi._pin);
1482 }
1483 
1484 // Function: set_load
set_load(std::string name,Split m,Tran t,std::optional<float> v)1485 Timer& Timer::set_load(std::string name, Split m, Tran t, std::optional<float> v) {
1486 
1487   std::scoped_lock lock(_mutex);
1488 
1489   auto task = _taskflow.emplace([this, name=std::move(name), m, t, v] () {
1490     if(auto itr = _pos.find(name); itr != _pos.end()) {
1491       _set_load(itr->second, m, t, v);
1492     }
1493     else {
1494       OT_LOGE("can't set load (PO ", name, " not found)");
1495     }
1496   });
1497 
1498   _add_to_lineage(task);
1499 
1500   return *this;
1501 }
1502 
1503 // Procedure: _set_load
_set_load(PrimaryOutput & po,Split m,Tran t,std::optional<float> v)1504 void Timer::_set_load(PrimaryOutput& po, Split m, Tran t, std::optional<float> v) {
1505 
1506   po._load[m][t] = v ? *v : 0.0f;
1507 
1508   // Update the net load
1509   if(auto net = po._pin._net) {
1510     net->_rc_timing_updated = false;
1511   }
1512 
1513   // Enable the timing propagation.
1514   for(auto arc : po._pin._fanin) {
1515     _insert_frontier(arc->_from);
1516   }
1517   _insert_frontier(po._pin);
1518 }
1519 
1520 
1521 };  // end of namespace ot. -----------------------------------------------------------------------
1522 
1523 
1524 
1525 
1526