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 = π
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