1 /*
2 BSD 3-Clause License
3
4 Copyright (c) 2020, The Regents of the University of Minnesota
5
6 All rights reserved.
7
8 Redistribution and use in source and binary forms, with or without
9 modification, are permitted provided that the following conditions are met:
10
11 * Redistributions of source code must retain the above copyright notice, this
12 list of conditions and the following disclaimer.
13
14 * Redistributions in binary form must reproduce the above copyright notice,
15 this list of conditions and the following disclaimer in the documentation
16 and/or other materials provided with the distribution.
17
18 * Neither the name of the copyright holder nor the names of its
19 contributors may be used to endorse or promote products derived from
20 this software without specific prior written permission.
21
22 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
26 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
28 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33 #include <vector>
34 #include <queue>
35 #include <math.h>
36 #include <cmath>
37 #include <iostream>
38 #include <stdlib.h>
39 #include <map>
40 #include <fstream>
41 #include <iomanip>
42 #include <iostream>
43 #include <time.h>
44 #include <sstream>
45 #include <iterator>
46 #include <string>
47
48 #include <Eigen/Sparse>
49 #include <Eigen/SparseLU>
50 #include "opendb/db.h"
51 #include "get_voltage.h"
52 #include "ir_solver.h"
53 #include "node.h"
54 #include "gmat.h"
55 #include "get_power.h"
56
57 namespace psm {
58 using odb::dbBlock;
59 using odb::dbBox;
60 using odb::dbChip;
61 using odb::dbDatabase;
62 using odb::dbInst;
63 using odb::dbNet;
64 using odb::dbSBox;
65 using odb::dbSet;
66 using odb::dbSigType;
67 using odb::dbSWire;
68 using odb::dbTech;
69 using odb::dbTechLayer;
70 using odb::dbTechLayerDir;
71 using odb::dbVia;
72 using odb::dbViaParams;
73
74 using std::endl;
75 using std::get;
76 using std::ifstream;
77 using std::make_pair;
78 using std::make_tuple;
79 using std::map;
80 using std::ofstream;
81 using std::pair;
82 using std::queue;
83 using std::setprecision;
84 using std::stod;
85 using std::string;
86 using std::stringstream;
87 using std::to_string;
88 using std::tuple;
89 using std::vector;
90
91 using Eigen::Map;
92 using Eigen::SparseLU;
93 using Eigen::SparseMatrix;
94 using Eigen::Success;
95 using Eigen::VectorXd;
96
97 //! Returns the created G matrix for the design
98 /*
99 * \return G Matrix
100 */
GetGMat()101 GMat* IRSolver::GetGMat()
102 {
103 return m_Gmat;
104 }
105
106 //! Returns current map represented as a 1D vector
107 /*
108 * \return J vector
109 */
GetJ()110 vector<double> IRSolver::GetJ()
111 {
112 return m_J;
113 }
114
115 //! Function to solve for voltage using SparseLU
SolveIR()116 void IRSolver::SolveIR()
117 {
118 if (!m_connection) {
119 m_logger->warn(utl::PSM,
120 8,
121 "Powergrid is not connected to all instances, therefore the "
122 "IR Solver may not be accurate. LVS may also fail.");
123 }
124 int unit_micron = (m_db->getTech())->getDbUnitsPerMicron();
125 clock_t t1, t2;
126 CscMatrix* Gmat = m_Gmat->GetGMat();
127 // fill A
128 int nnz = Gmat->nnz;
129 int m = Gmat->num_rows;
130 int n = Gmat->num_cols;
131 double* values = &(Gmat->values[0]);
132 int* row_idx = &(Gmat->row_idx[0]);
133 int* col_ptr = &(Gmat->col_ptr[0]);
134 Map<SparseMatrix<double>> A(Gmat->num_rows,
135 Gmat->num_cols,
136 Gmat->nnz,
137 col_ptr, // read-write
138 row_idx,
139 values);
140
141 vector<double> J = GetJ();
142 Map<VectorXd> b(J.data(), J.size());
143 VectorXd x;
144 SparseLU<SparseMatrix<double>> solver;
145 debugPrint(m_logger, utl::PSM, "IR Solver", 1, "Factorizing the G matrix");
146 solver.compute(A);
147 if (solver.info() != Success) {
148 // decomposition failed
149 m_logger->error(utl::PSM, 10, "LU factorization of the G Matrix failed.");
150 }
151 debugPrint(m_logger, utl::PSM, "IR Solver", 1, "Solving system of equations GV=J");
152 x = solver.solve(b);
153 if (solver.info() != Success) {
154 // solving failed
155 m_logger->error(utl::PSM, 12, "Solving V = inv(G)*J failed.");
156 } else {
157 debugPrint(m_logger, utl::PSM, "IR Solver", 1, "Solving system of equations GV=J complete");
158 }
159 ofstream ir_report;
160 ir_report.open(m_out_file);
161 ir_report << "Instance name, "
162 << " X location, "
163 << " Y location, "
164 << " Voltage "
165 << "\n";
166 int num_nodes = m_Gmat->GetNumNodes();
167 int node_num = 0;
168 double sum_volt = 0;
169 wc_voltage = supply_voltage_src;
170 while (node_num < num_nodes) {
171 Node* node = m_Gmat->GetNode(node_num);
172 double volt = x(node_num);
173 sum_volt = sum_volt + volt;
174 if (m_power_net_type == dbSigType::POWER) {
175 if (volt < wc_voltage) {
176 wc_voltage = volt;
177 }
178 } else {
179 if (volt > wc_voltage) {
180 wc_voltage = volt;
181 }
182 }
183 node->SetVoltage(volt);
184 node_num++;
185 if (node->HasInstances()) {
186 NodeLoc node_loc = node->GetLoc();
187 float loc_x = ((float) node_loc.first) / ((float) unit_micron);
188 float loc_y = ((float) node_loc.second) / ((float) unit_micron);
189 vector<dbInst*> insts = node->GetInstances();
190 vector<dbInst*>::iterator inst_it;
191 if (m_out_file != "") {
192 for (inst_it = insts.begin(); inst_it != insts.end(); inst_it++) {
193 ir_report << (*inst_it)->getName() << ", " << loc_x << ", " << loc_y
194 << ", " << setprecision(6) << volt << "\n";
195 }
196 }
197 }
198 }
199 ir_report << endl;
200 ir_report.close();
201 avg_voltage = sum_volt / num_nodes;
202 if (m_em_flag == 1) {
203 map<GMatLoc, double>::iterator it;
204 DokMatrix* Gmat_dok = m_Gmat->GetGMatDOK();
205 int resistance_number = 0;
206 max_cur = 0;
207 double sum_cur = 0;
208 ofstream em_report;
209 if (m_em_out_file != "") {
210 em_report.open(m_em_out_file);
211 em_report << "Segment name, "
212 << " Current, "
213 << " Node 1, "
214 << " Node 2 "
215 << "\n";
216 }
217 NodeLoc node_loc;
218 for (it = Gmat_dok->values.begin(); it != Gmat_dok->values.end(); it++) {
219 NodeIdx col = (it->first).first;
220 NodeIdx row = (it->first).second;
221 if (col <= row) {
222 continue; // ignore lower half and diagonal as matrix is symmetric
223 }
224 double cond = it->second; // get cond value
225 if (abs(cond) < 1e-15) { // ignore if an empty cell
226 continue;
227 }
228 string net_name = m_power_net;
229 if (col < num_nodes) { // resistances
230 double resistance = -1 / cond;
231
232 Node* node1 = m_Gmat->GetNode(col);
233 Node* node2 = m_Gmat->GetNode(row);
234 node_loc = node1->GetLoc();
235 int x1 = node_loc.first;
236 int y1 = node_loc.second;
237 int l1 = node1->GetLayerNum();
238 string node1_name = net_name + "_" + to_string(x1) + "_" + to_string(y1)
239 + "_" + to_string(l1);
240
241 node_loc = node2->GetLoc();
242 int x2 = node_loc.first;
243 int y2 = node_loc.second;
244 int l2 = node2->GetLayerNum();
245 string node2_name = net_name + "_" + to_string(x2) + "_" + to_string(y2)
246 + "_" + to_string(l2);
247
248 string segment_name = "seg_" + to_string(resistance_number);
249
250 double v1 = node1->GetVoltage();
251 double v2 = node2->GetVoltage();
252 double seg_cur;
253 seg_cur = (v1 - v2) / resistance;
254 sum_cur += abs(seg_cur);
255 if (m_em_out_file != "") {
256 em_report << segment_name << ", " << setprecision(3) << seg_cur
257 << ", " << node1_name << ", " << node2_name << endl;
258 }
259 seg_cur = abs(seg_cur);
260 if (seg_cur > max_cur) {
261 max_cur = seg_cur;
262 }
263 resistance_number++;
264 }
265 } // for gmat values
266 avg_cur = sum_cur / resistance_number;
267 num_res = resistance_number;
268
269 } // enable em
270 }
271
272 //! Function to add C4 bumps to the G matrix
AddC4Bump()273 bool IRSolver::AddC4Bump()
274 {
275 if (m_C4Bumps.size() == 0) {
276 m_logger->error(utl::PSM, 14, "Number of voltage sources cannot be 0.");
277 }
278 m_logger->info(utl::PSM, 64, "Number of voltage sources = {}",m_C4Bumps.size());
279 for (size_t it = 0; it < m_C4Nodes.size(); ++it) {
280 NodeIdx node_loc = m_C4Nodes[it].first;
281 double voltage_value = m_C4Nodes[it].second;
282 Node* c4_node = m_Gmat->GetNode(node_loc);
283 m_Gmat->AddC4Bump(node_loc, it); // add the 0th bump
284 m_J.push_back(voltage_value); // push back first vdd
285 NodeLoc c4_node_loc = c4_node->GetLoc();
286 }
287 return true;
288 }
289
290 //! Function that parses the Vsrc file
ReadC4Data()291 void IRSolver::ReadC4Data()
292 {
293 int unit_micron = (m_db->getTech())->getDbUnitsPerMicron();
294 if (m_vsrc_file != "") {
295 m_logger->info(utl::PSM,
296 15,
297 "Reading location of VDD and VSS sources from {}.",
298 m_vsrc_file);
299 ifstream file(m_vsrc_file);
300 string line = "";
301 // Iterate through each line and split the content using delimiter
302 while (getline(file, line)) {
303 tuple<int, int, int, double> c4_bump;
304 int first, second, size;
305 stringstream X(line);
306 string val;
307 for (int i = 0; i < 4; ++i) {
308 getline(X, val, ',');
309 if (i == 0) {
310 first = (int) (unit_micron * stod(val));
311 } else if (i == 1) {
312 second = (int) (unit_micron * stod(val));
313 } else if (i == 2) {
314 size = (int) (unit_micron * stod(val));
315 } else {
316 supply_voltage_src = stod(val);
317 }
318 }
319 m_C4Bumps.push_back(make_tuple(first, second, size, supply_voltage_src));
320 }
321 file.close();
322 } else {
323 m_logger->warn(utl::PSM,
324 16,
325 "Voltage pad location (vsrc) file not specified, defaulting "
326 "pad location to checkerboard pattern on core area.");
327 dbChip* chip = m_db->getChip();
328 dbBlock* block = chip->getBlock();
329 odb::Rect coreRect;
330 block->getCoreArea(coreRect);
331 int coreW = coreRect.xMax() - coreRect.xMin();
332 int coreL = coreRect.yMax() - coreRect.yMin();
333 odb::Rect dieRect;
334 block->getDieArea(dieRect);
335 int dieW = dieRect.xMax() - dieRect.xMin();
336 int dieL = dieRect.xMax() - dieRect.xMin();
337 int offset_x = coreRect.xMin() - dieRect.xMin();
338 int offset_y = coreRect.yMin() - dieRect.yMin();
339 if (m_bump_pitch_x == 0) {
340 m_bump_pitch_x = m_bump_pitch_default * unit_micron;
341 m_logger->warn(
342 utl::PSM,
343 17,
344 "X direction bump pitch is not specified, defaulting to {}um.",
345 m_bump_pitch_default);
346 }
347 if (m_bump_pitch_y == 0) {
348 m_bump_pitch_y = m_bump_pitch_default * unit_micron;
349 m_logger->warn(
350 utl::PSM,
351 18,
352 "Y direction bump pitch is not specified, defaulting to {}um.",
353 m_bump_pitch_default);
354 }
355 if (!m_net_voltage_map.empty()
356 && m_net_voltage_map.count(m_power_net) > 0) {
357 supply_voltage_src = m_net_voltage_map.at(m_power_net);
358 } else {
359 m_logger->warn(utl::PSM,
360 19,
361 "Voltage on net {} is not explicitly set.",
362 m_power_net);
363 pair<double, double> supply_voltages = GetSupplyVoltage();
364 dbNet* power_net = block->findNet(m_power_net.data());
365 if (power_net == NULL) {
366 m_logger->error(utl::PSM,
367 20,
368 "Cannot find net {} in the design. Please provide a "
369 "valid VDD/VSS net.",
370 m_power_net);
371 }
372 m_power_net_type = power_net->getSigType();
373 if (m_power_net_type == dbSigType::GROUND) {
374 supply_voltage_src = supply_voltages.second;
375 m_logger->warn(utl::PSM,
376 21,
377 "Using voltage {:4.3f}V for ground network.",
378 supply_voltage_src);
379 } else {
380 supply_voltage_src = supply_voltages.first;
381 m_logger->warn(utl::PSM,
382 22,
383 "Using voltage {:4.3f}V for VDD network.",
384 supply_voltage_src);
385 }
386 }
387 int x_cor, y_cor;
388 if (coreW < m_bump_pitch_x || coreL < m_bump_pitch_y) {
389 float to_micron = 1.0f/unit_micron;
390 m_logger->warn(utl::PSM, 63, "Specified bump pitches of {:4.3f} and {:4.3f} are less than core width of {:4.3f} or core height of "
391 "{:4.3f}. Changing bump location to the center of the die at ({:4.3f}, {:4.3f})",
392 m_bump_pitch_x * to_micron, m_bump_pitch_y * to_micron,
393 coreW * to_micron, coreL * to_micron, (coreW * to_micron)/2, (coreL * to_micron)/2);
394 x_cor = coreW/2;
395 y_cor = coreL/2;
396 m_C4Bumps.push_back(make_tuple(x_cor, y_cor, m_bump_size * unit_micron, supply_voltage_src));
397 }
398 int num_b_x = coreW / m_bump_pitch_x;
399 int num_b_y = coreL / m_bump_pitch_y;
400 m_logger->warn(utl::PSM,
401 65,
402 "VSRC location not specified using default checkerboard pattern with one VDD every"
403 "size bumps in x-direction and one in two bumps in the y-direction");
404 for (int i = 0; i < num_b_y; i++) {
405 for (int j = 0; j < num_b_x; j=j+6) {
406 x_cor = (m_bump_pitch_x * j) + (((2 * i) % 6) * m_bump_pitch_x)
407 + offset_x;
408 y_cor = (m_bump_pitch_y * i) + offset_y;
409 if (x_cor <= coreW && y_cor <= coreL) {
410 m_C4Bumps.push_back(make_tuple(
411 x_cor, y_cor, m_bump_size * unit_micron, supply_voltage_src));
412 }
413 }
414 }
415 }
416 }
417
418 //! Function to create a J vector from the current map
CreateJ()419 bool IRSolver::CreateJ()
420 { // take current_map as an input?
421 int num_nodes = m_Gmat->GetNumNodes();
422 m_J.resize(num_nodes, 0);
423
424 vector<pair<string, double>> power_report = GetPower();
425 dbChip* chip = m_db->getChip();
426 dbBlock* block = chip->getBlock();
427 for (vector<pair<string, double>>::iterator it = power_report.begin();
428 it != power_report.end();
429 ++it) {
430 dbInst* inst = block->findInst(it->first.c_str());
431 if (inst == NULL) {
432 m_logger->warn(
433 utl::PSM, 23, "Instance {} not found within database.", it->first);
434 continue;
435 }
436 int x, y;
437 inst->getLocation(x, y);
438 // cout << "Got location" <<endl;
439 int l = m_bottom_layer; // atach to the bottom most routing layer
440 Node* node_J = m_Gmat->GetNode(x, y, l, true);
441 NodeLoc node_loc = node_J->GetLoc();
442 if (abs(node_loc.first - x) > m_node_density
443 || abs(node_loc.second - y) > m_node_density) {
444 m_logger->warn(utl::PSM,
445 24,
446 "Instance {} curent node at ({}, {}) at layer {} have "
447 "been moved from ({}, {}).",
448 it->first,
449 node_loc.first,
450 node_loc.second,
451 l,
452 x,
453 y);
454 }
455 // Both these lines will change in the future for multiple power domains
456 node_J->AddCurrentSrc(it->second);
457 node_J->AddInstance(inst);
458 }
459 for (int i = 0; i < num_nodes; ++i) {
460 Node* node_J = m_Gmat->GetNode(i);
461 if (m_power_net_type == dbSigType::GROUND) {
462 m_J[i] = (node_J->GetCurrent());
463 } else {
464 m_J[i] = -1 * (node_J->GetCurrent());
465 }
466 }
467 debugPrint(m_logger, utl::PSM, "IR Solver", 1, "Created J vector");
468 //m_logger->info(utl::PSM, 25, "Created J vector");
469 return true;
470 }
471
472 //! Function to create a G matrix using the nodes
CreateGmat(bool connection_only)473 bool IRSolver::CreateGmat(bool connection_only)
474 {
475 debugPrint(m_logger, utl::PSM, "G Matrix", 1, "Creating G matrix");
476 vector<Node*> node_vector;
477 dbTech* tech = m_db->getTech();
478 // dbSet<dbTechLayer> layers = tech->getLayers();
479 dbSet<dbTechLayer>::iterator litr;
480 int unit_micron = tech->getDbUnitsPerMicron();
481 int num_routing_layers = tech->getRoutingLayerCount();
482
483 m_Gmat = new GMat(num_routing_layers, m_logger);
484 dbChip* chip = m_db->getChip();
485 dbBlock* block = chip->getBlock();
486 dbSet<dbNet> nets = block->getNets();
487 dbNet* power_net = block->findNet(m_power_net.data());
488 if (power_net == NULL) {
489 m_logger->error(utl::PSM,
490 27,
491 " Cannot find net {} in the design. Please provide a valid "
492 "VDD/VSS net.",
493 m_power_net);
494 }
495 m_power_net_type = power_net->getSigType();
496 vector<dbNet*> power_nets;
497 int num_wires = 0;
498 debugPrint(m_logger, utl::PSM, "G Matrix", 1, "Extracting power stripes on net {}", power_net->getName());
499 power_nets.push_back(power_net);
500
501 if (power_nets.size() == 0) {
502 m_logger->error(
503 utl::PSM,
504 29,
505 "No power stripes found in design. Power grid checker will not run.");
506 }
507 vector<dbNet*>::iterator vIter;
508 for (vIter = power_nets.begin(); vIter != power_nets.end(); ++vIter) {
509 dbNet* curDnet = *vIter;
510 dbSet<dbSWire> swires = curDnet->getSWires();
511 dbSet<dbSWire>::iterator sIter;
512 for (sIter = swires.begin(); sIter != swires.end(); ++sIter) {
513 dbSWire* curSWire = *sIter;
514 dbSet<dbSBox> wires = curSWire->getWires();
515 dbSet<dbSBox>::iterator wIter;
516 for (wIter = wires.begin(); wIter != wires.end(); ++wIter) {
517 num_wires++;
518 dbSBox* curWire = *wIter;
519 int l;
520 dbTechLayerDir::Value layer_dir;
521 if (curWire->isVia()) {
522 dbVia* via = curWire->getBlockVia();
523 dbTechLayer* via_layer = via->getTopLayer();
524 l = via_layer->getRoutingLevel();
525 layer_dir = via_layer->getDirection();
526 } else {
527 dbTechLayer* wire_layer = curWire->getTechLayer();
528 l = wire_layer->getRoutingLevel();
529 layer_dir = wire_layer->getDirection();
530 if (l < m_bottom_layer) {
531 m_bottom_layer = l;
532 m_bottom_layer_dir = layer_dir;
533 }
534 }
535 if (l > m_top_layer) {
536 m_top_layer = l;
537 m_top_layer_dir = layer_dir;
538 }
539 }
540 }
541 }
542 for (vIter = power_nets.begin(); vIter != power_nets.end(); ++vIter) {
543 dbNet* curDnet = *vIter;
544 dbSet<dbSWire> swires = curDnet->getSWires();
545 dbSet<dbSWire>::iterator sIter;
546 for (sIter = swires.begin(); sIter != swires.end(); ++sIter) {
547 dbSWire* curSWire = *sIter;
548 dbSet<dbSBox> wires = curSWire->getWires();
549 dbSet<dbSBox>::iterator wIter;
550 for (wIter = wires.begin(); wIter != wires.end(); ++wIter) {
551 dbSBox* curWire = *wIter;
552 if (curWire->isVia()) {
553 dbVia* via = curWire->getBlockVia();
554 dbBox* via_bBox = via->getBBox();
555 int check_params = via->hasParams();
556 int x_cut_size = 0;
557 int y_cut_size = 0;
558 int x_bottom_enclosure = 0;
559 int y_bottom_enclosure = 0;
560 int x_top_enclosure = 0;
561 int y_top_enclosure = 0;
562 if (check_params == 1) {
563 dbViaParams params;
564 via->getViaParams(params);
565 x_cut_size = params.getXCutSize();
566 y_cut_size = params.getYCutSize();
567 x_bottom_enclosure = params.getXBottomEnclosure();
568 y_bottom_enclosure = params.getYBottomEnclosure();
569 x_top_enclosure = params.getXTopEnclosure();
570 y_top_enclosure = params.getYTopEnclosure();
571 }
572 BBox bBox
573 = make_pair((via_bBox->getDX()) / 2, (via_bBox->getDY()) / 2);
574 int x, y;
575 curWire->getViaXY(x, y);
576 dbTechLayer* via_layer = via->getBottomLayer();
577 dbTechLayerDir::Value layer_dir = via_layer->getDirection();
578 int l = via_layer->getRoutingLevel();
579 int x_loc1, x_loc2, y_loc1, y_loc2;
580 if (m_bottom_layer != l
581 && l != m_top_layer) { // do not set for top and bottom layers
582 if (layer_dir == dbTechLayerDir::Value::HORIZONTAL) {
583 y_loc1 = y;
584 y_loc2 = y;
585 x_loc1 = x - (x_bottom_enclosure + x_cut_size / 2);
586 x_loc2 = x + (x_bottom_enclosure + x_cut_size / 2);
587 } else {
588 y_loc1 = y - (y_bottom_enclosure + y_cut_size / 2);
589 y_loc2 = y + (y_bottom_enclosure + y_cut_size / 2);
590 x_loc1 = x;
591 x_loc2 = x;
592 }
593 m_Gmat->SetNode(x_loc1, y_loc1, l, make_pair(0, 0));
594 m_Gmat->SetNode(x_loc2, y_loc2, l, make_pair(0, 0));
595 m_Gmat->SetNode(x, y, l, bBox);
596 }
597 via_layer = via->getTopLayer();
598 l = via_layer->getRoutingLevel();
599
600 // TODO this may count the stripe conductance twice but is needed to
601 // fix a staggered stacked via
602 layer_dir = via_layer->getDirection();
603 if (m_bottom_layer != l
604 && l != m_top_layer) { // do not set for top and bottom layers
605 if (layer_dir == dbTechLayerDir::Value::HORIZONTAL) {
606 y_loc1 = y;
607 y_loc2 = y;
608 x_loc1 = x - (x_top_enclosure + x_cut_size / 2);
609 x_loc2 = x + (x_top_enclosure + x_cut_size / 2);
610 } else {
611 y_loc1 = y - (y_top_enclosure + y_cut_size / 2);
612 y_loc2 = y + (y_top_enclosure + y_cut_size / 2);
613 x_loc1 = x;
614 x_loc2 = x;
615 }
616 m_Gmat->SetNode(x_loc1, y_loc1, l, make_pair(0, 0));
617 m_Gmat->SetNode(x_loc2, y_loc2, l, make_pair(0, 0));
618 m_Gmat->SetNode(x, y, l, bBox);
619 }
620 } else {
621 int x_loc1, x_loc2, y_loc1, y_loc2;
622 dbTechLayer* wire_layer = curWire->getTechLayer();
623 int l = wire_layer->getRoutingLevel();
624 dbTechLayerDir::Value layer_dir = wire_layer->getDirection();
625 if (l == m_bottom_layer) {
626 layer_dir = dbTechLayerDir::Value::HORIZONTAL;
627 }
628 if (layer_dir == dbTechLayerDir::Value::HORIZONTAL) {
629 y_loc1 = (curWire->yMin() + curWire->yMax()) / 2;
630 y_loc2 = (curWire->yMin() + curWire->yMax()) / 2;
631 x_loc1 = curWire->xMin();
632 x_loc2 = curWire->xMax();
633 } else {
634 x_loc1 = (curWire->xMin() + curWire->xMax()) / 2;
635 x_loc2 = (curWire->xMin() + curWire->xMax()) / 2;
636 y_loc1 = curWire->yMin();
637 y_loc2 = curWire->yMax();
638 }
639 if (l == m_bottom_layer
640 || l == m_top_layer) { // special case for bottom and top layers
641 // we design a dense grid
642 if (layer_dir == dbTechLayerDir::Value::HORIZONTAL) {
643 int x_i;
644 x_loc1 = (x_loc1 / m_node_density)
645 * m_node_density; // quantize the horizontal direction
646 x_loc2 = (x_loc2 / m_node_density)
647 * m_node_density; // quantize the horizontal direction
648 for (x_i = x_loc1; x_i <= x_loc2; x_i = x_i + m_node_density) {
649 m_Gmat->SetNode(x_i, y_loc1, l, make_pair(0, 0));
650 }
651 } else {
652 y_loc1 = (y_loc1 / m_node_density)
653 * m_node_density; // quantize the vertical direction
654 y_loc2 = (y_loc2 / m_node_density)
655 * m_node_density; // quantize the vertical direction
656 int y_i;
657 for (y_i = y_loc1; y_i <= y_loc2; y_i = y_i + m_node_density) {
658 m_Gmat->SetNode(x_loc1, y_i, l, make_pair(0, 0));
659 }
660 }
661 } else { // add end nodes
662 m_Gmat->SetNode(x_loc1, y_loc1, l, make_pair(0, 0));
663 m_Gmat->SetNode(x_loc2, y_loc2, l, make_pair(0, 0));
664 }
665 }
666 }
667 }
668 }
669 // insert c4 bumps as nodes
670 int num_C4 = 0;
671 for (size_t it = 0; it < m_C4Bumps.size(); ++it) {
672 int x = get<0>(m_C4Bumps[it]);
673 int y = get<1>(m_C4Bumps[it]);
674 int size = get<2>(m_C4Bumps[it]);
675 double v = get<3>(m_C4Bumps[it]);
676 vector<Node*> RDL_nodes;
677 RDL_nodes = m_Gmat->GetRDLNodes(m_top_layer,
678 m_top_layer_dir,
679 x - size / 2,
680 x + size / 2,
681 y - size / 2,
682 y + size / 2);
683 if (RDL_nodes.empty() == true) {
684 Node* node = m_Gmat->GetNode(x, y, m_top_layer, true);
685 NodeLoc node_loc = node->GetLoc();
686 double new_loc1 = ((double) node_loc.first) / ((double) unit_micron);
687 double new_loc2 = ((double) node_loc.second) / ((double) unit_micron);
688 double old_loc1 = ((double) x) / ((double) unit_micron);
689 double old_loc2 = ((double) y) / ((double) unit_micron);
690 double old_size = ((double) size) / ((double) unit_micron);
691 m_logger->warn(utl::PSM,
692 30,
693 "Vsrc location at ({:4.3f}um, {:4.3f}um) and size ={:4.3f}um, is not located on a power stripe. Moving to closest stripe at ({:4.3f}um, {:4.3f}um).",
694 old_loc1,
695 old_loc2,
696 old_size,
697 new_loc1,
698 new_loc2);
699 RDL_nodes = m_Gmat->GetRDLNodes(m_top_layer,
700 m_top_layer_dir,
701 node_loc.first - size / 2,
702 node_loc.first + size / 2,
703 node_loc.second - size / 2,
704 node_loc.second + size / 2);
705 }
706 vector<Node*>::iterator node_it;
707 for (node_it = RDL_nodes.begin(); node_it != RDL_nodes.end(); ++node_it) {
708 Node* node = *node_it;
709 m_C4Nodes.push_back(make_pair(node->GetGLoc(), v));
710 num_C4++;
711 }
712 }
713 // All new nodes must be inserted by this point
714 // initialize G Matrix
715 m_logger->info(utl::PSM,
716 31,
717 "Number of PDN nodes on net {} = {}.",
718 m_power_net,
719 m_Gmat->GetNumNodes());
720 m_Gmat->InitializeGmatDok(num_C4);
721 for (vIter = power_nets.begin(); vIter != power_nets.end();
722 ++vIter) { // only 1 is expected?
723 dbNet* curDnet = *vIter;
724 dbSet<dbSWire> swires = curDnet->getSWires();
725 dbSet<dbSWire>::iterator sIter;
726 for (sIter = swires.begin(); sIter != swires.end();
727 ++sIter) { // only 1 is expected?
728 dbSWire* curSWire = *sIter;
729 dbSet<dbSBox> wires = curSWire->getWires();
730 dbSet<dbSBox>::iterator wIter;
731 for (wIter = wires.begin(); wIter != wires.end(); ++wIter) {
732 dbSBox* curWire = *wIter;
733 if (curWire->isVia()) {
734 dbVia* via = curWire->getBlockVia();
735 int num_via_rows = 1;
736 int num_via_cols = 1;
737 int check_params = via->hasParams();
738 int x_cut_size = 0;
739 int y_cut_size = 0;
740 int x_bottom_enclosure = 0;
741 int y_bottom_enclosure = 0;
742 int x_top_enclosure = 0;
743 int y_top_enclosure = 0;
744 if (check_params == 1) {
745 dbViaParams params;
746 via->getViaParams(params);
747 num_via_rows = params.getNumCutRows();
748 num_via_cols = params.getNumCutCols();
749 x_cut_size = params.getXCutSize();
750 y_cut_size = params.getYCutSize();
751 x_bottom_enclosure = params.getXBottomEnclosure();
752 y_bottom_enclosure = params.getYBottomEnclosure();
753 x_top_enclosure = params.getXTopEnclosure();
754 y_top_enclosure = params.getYTopEnclosure();
755 }
756 dbBox* via_bBox = via->getBBox();
757 BBox bBox
758 = make_pair((via_bBox->getDX()) / 2, (via_bBox->getDY()) / 2);
759 int x, y;
760 curWire->getViaXY(x, y);
761 dbTechLayer* via_layer = via->getBottomLayer();
762 int l = via_layer->getRoutingLevel();
763
764 double R = via_layer->getUpperLayer()->getResistance();
765 R = R / (num_via_rows * num_via_cols);
766 if (!CheckValidR(R) && !connection_only) {
767 m_logger->error(utl::PSM,
768 35, "{} resistance not found in DB. Check the LEF or set it with set_layer_rc command.", via_layer->getName());
769 }
770 bool top_or_bottom = ((l == m_bottom_layer) || (l == m_top_layer));
771 Node* node_bot = m_Gmat->GetNode(x, y, l, top_or_bottom);
772 NodeLoc node_loc = node_bot->GetLoc();
773 if (abs(node_loc.first - x) > m_node_density
774 || abs(node_loc.second - y) > m_node_density) {
775 m_logger->warn(utl::PSM,
776 32,
777 "Node at ({}, {}) and layer {} moved from ({}, {}).",
778 node_loc.first,
779 node_loc.second,
780 l,
781 x,
782 y);
783 }
784
785 via_layer = via->getTopLayer();
786 l = via_layer->getRoutingLevel();
787 top_or_bottom = ((l == m_bottom_layer) || (l == m_top_layer));
788 Node* node_top = m_Gmat->GetNode(x, y, l, top_or_bottom);
789 node_loc = node_top->GetLoc();
790 if (abs(node_loc.first - x) > m_node_density
791 || abs(node_loc.second - y) > m_node_density) {
792 m_logger->warn(utl::PSM,
793 33,
794 "Node at ({}, {}) and layer {} moved from ({}, {}).",
795 node_loc.first,
796 node_loc.second,
797 l,
798 x,
799 y);
800 }
801
802 if (node_bot == nullptr || node_top == nullptr) {
803 m_logger->error(
804 utl::PSM,
805 34,
806 "Unxpected condition. Null pointer received for node.");
807 } else {
808 if (R <= 1e-12) { // if the resitance was not set.
809 m_Gmat->SetConductance(node_bot, node_top, 0);
810 } else {
811 m_Gmat->SetConductance(node_bot, node_top, 1 / R);
812 }
813 }
814
815 via_layer = via->getBottomLayer();
816 dbTechLayerDir::Value layer_dir = via_layer->getDirection();
817 l = via_layer->getRoutingLevel();
818 if (l != m_bottom_layer) {
819 double rho = via_layer->getResistance();
820 if (!CheckValidR(rho) && !connection_only) {
821 m_logger->error( utl::PSM,
822 36, "Layer {} per-unit resistance not found in DB. Check the LEF or set it with a set_layer_rc -layer command.",via_layer->getName());
823 }
824 int x_loc1, x_loc2, y_loc1, y_loc2;
825 if (layer_dir == dbTechLayerDir::Value::HORIZONTAL) {
826 y_loc1 = y - y_cut_size / 2;
827 y_loc2 = y + y_cut_size / 2;
828 x_loc1 = x - (x_bottom_enclosure + x_cut_size / 2);
829 x_loc2 = x + (x_bottom_enclosure + x_cut_size / 2);
830 } else {
831 y_loc1 = y - (y_bottom_enclosure + y_cut_size / 2);
832 y_loc2 = y + (y_bottom_enclosure + y_cut_size / 2);
833 x_loc1 = x - x_cut_size / 2;
834 x_loc2 = x + x_cut_size / 2;
835 }
836 m_Gmat->GenerateStripeConductance(via_layer->getRoutingLevel(),
837 layer_dir,
838 x_loc1,
839 x_loc2,
840 y_loc1,
841 y_loc2,
842 rho);
843 }
844 via_layer = via->getTopLayer();
845 layer_dir = via_layer->getDirection();
846 l = via_layer->getRoutingLevel();
847 if (l != m_top_layer) {
848 double rho = via_layer->getResistance();
849 if (!CheckValidR(rho) && !connection_only) {
850 m_logger->error( utl::PSM,
851 37, "Layer {} per-unit resistance not found in DB. Check the LEF or set it with a set_layer_rc -layer command.",via_layer->getName());
852 }
853 int x_loc1, x_loc2, y_loc1, y_loc2;
854 if (layer_dir == dbTechLayerDir::Value::HORIZONTAL) {
855 y_loc1 = y - y_cut_size / 2;
856 y_loc2 = y + y_cut_size / 2;
857 x_loc1 = x - (x_top_enclosure + x_cut_size / 2);
858 x_loc2 = x + (x_top_enclosure + x_cut_size / 2);
859 } else {
860 y_loc1 = y - (y_top_enclosure + y_cut_size / 2);
861 y_loc2 = y + (y_top_enclosure + y_cut_size / 2);
862 x_loc1 = x - x_cut_size / 2;
863 x_loc2 = x + x_cut_size / 2;
864 }
865 m_Gmat->GenerateStripeConductance(via_layer->getRoutingLevel(),
866 layer_dir,
867 x_loc1,
868 x_loc2,
869 y_loc1,
870 y_loc2,
871 rho);
872 }
873
874 } else {
875 dbTechLayer* wire_layer = curWire->getTechLayer();
876 int l = wire_layer->getRoutingLevel();
877 double rho = wire_layer->getResistance();
878 if (!CheckValidR(rho) && !connection_only) {
879 m_logger->error( utl::PSM,
880 66, "Layer {} per-unit resistance not found in DB. Check the LEF or set it with a set_layer_rc -layer command.",wire_layer->getName());
881 }
882 dbTechLayerDir::Value layer_dir = wire_layer->getDirection();
883 if (l == m_bottom_layer) { // ensure that the bootom layer(rail) is
884 // horizontal
885 layer_dir = dbTechLayerDir::Value::HORIZONTAL;
886 }
887 int x_loc1 = curWire->xMin();
888 int x_loc2 = curWire->xMax();
889 int y_loc1 = curWire->yMin();
890 int y_loc2 = curWire->yMax();
891 if (l == m_bottom_layer
892 || l == m_top_layer) { // special case for bottom and top layers
893 // we design a dense grid
894 if (layer_dir == dbTechLayerDir::Value::HORIZONTAL) {
895 x_loc1 = (x_loc1 / m_node_density)
896 * m_node_density; // quantize the horizontal direction
897 x_loc2 = (x_loc2 / m_node_density)
898 * m_node_density; // quantize the horizontal direction
899 } else {
900 y_loc1 = (y_loc1 / m_node_density)
901 * m_node_density; // quantize the vertical direction
902 y_loc2 = (y_loc2 / m_node_density)
903 * m_node_density; // quantize the vertical direction
904 }
905 }
906 m_Gmat->GenerateStripeConductance(wire_layer->getRoutingLevel(),
907 layer_dir,
908 x_loc1,
909 x_loc2,
910 y_loc1,
911 y_loc2,
912 rho);
913 }
914 }
915 }
916 }
917 debugPrint(m_logger, utl::PSM, "G Matrix", 1, "G matrix created sucessfully.");
918 return true;
919 }
920
921
CheckValidR(double R)922 bool IRSolver::CheckValidR(double R) {
923 return R>=1e-12;
924 }
925
CheckConnectivity()926 bool IRSolver::CheckConnectivity()
927 {
928 vector<pair<NodeIdx, double>>::iterator c4_node_it;
929 int x, y;
930 CscMatrix* Amat = m_Gmat->GetAMat();
931 int num_nodes = m_Gmat->GetNumNodes();
932
933 dbTech* tech = m_db->getTech();
934 int unit_micron = tech->getDbUnitsPerMicron();
935
936 for (c4_node_it = m_C4Nodes.begin(); c4_node_it != m_C4Nodes.end();
937 c4_node_it++) {
938 Node* c4_node = m_Gmat->GetNode((*c4_node_it).first);
939 queue<Node*> node_q;
940 node_q.push(c4_node);
941 while (!node_q.empty()) {
942 NodeIdx col_loc, n_col_loc;
943 Node* node = node_q.front();
944 node_q.pop();
945 node->SetConnected();
946 NodeIdx col_num = node->GetGLoc();
947 col_loc = Amat->col_ptr[col_num];
948 if (col_num < Amat->col_ptr.size() - 1) {
949 n_col_loc = Amat->col_ptr[col_num + 1];
950 } else {
951 n_col_loc = Amat->row_idx.size();
952 }
953 vector<NodeIdx> col_vec(Amat->row_idx.begin() + col_loc,
954 Amat->row_idx.begin() + n_col_loc);
955
956 vector<NodeIdx>::iterator col_vec_it;
957 for (col_vec_it = col_vec.begin(); col_vec_it != col_vec.end();
958 col_vec_it++) {
959 if (*col_vec_it < num_nodes) {
960 Node* node_next = m_Gmat->GetNode(*col_vec_it);
961 if (!(node_next->GetConnected())) {
962 node_q.push(node_next);
963 }
964 }
965 }
966 }
967 }
968 int uncon_err_cnt = 0;
969 int uncon_err_flag = 0;
970 int uncon_inst_cnt = 0;
971 int uncon_inst_flag = 0;
972 vector<Node*> node_list = m_Gmat->GetAllNodes();
973 vector<Node*>::iterator node_list_it;
974 bool unconnected_node = false;
975 for (node_list_it = node_list.begin(); node_list_it != node_list.end();
976 node_list_it++) {
977 if (!(*node_list_it)->GetConnected()) {
978 uncon_err_cnt++;
979 NodeLoc node_loc = (*node_list_it)->GetLoc();
980 float loc_x = ((float) node_loc.first) / ((float) unit_micron);
981 float loc_y = ((float) node_loc.second) / ((float) unit_micron);
982 unconnected_node = true;
983 m_logger->warn(utl::PSM, 38, "Unconnected PDN node on net {} at location ({:4.3f}um, {:4.3f}um), layer: {}.",
984 m_power_net, loc_x, loc_y,(*node_list_it)->GetLayerNum());
985 if ((*node_list_it)->HasInstances()) {
986 vector<dbInst*> insts = (*node_list_it)->GetInstances();
987 vector<dbInst*>::iterator inst_it;
988 for (inst_it = insts.begin(); inst_it != insts.end(); inst_it++) {
989 uncon_inst_cnt++;
990 m_logger->warn(utl::PSM, 39,"Unconnected Instance {} at location ({:4.3f}um, {:4.3f}um) layer: {}.",
991 (*inst_it)->getName(), loc_x, loc_y, (*node_list_it)->GetLayerNum());
992 }
993 }
994 }
995 }
996 if (unconnected_node == false) {
997 m_logger->info(utl::PSM, 40, "All PDN stripes on net {} are connected.", m_power_net);
998 }
999 return !unconnected_node;
1000 }
1001
GetConnectionTest()1002 int IRSolver::GetConnectionTest()
1003 {
1004 if (m_connection) {
1005 return 1;
1006 } else {
1007 return 0;
1008 }
1009 }
1010
1011 //! Function to get the power value from OpenSTA
1012 /*
1013 *\return vector of pairs of instance name
1014 and its corresponding power value
1015 */
GetPower()1016 vector<pair<string, double>> IRSolver::GetPower()
1017 {
1018 PowerInst power_inst;
1019
1020 debugPrint(m_logger, utl::PSM, "IR Solver", 1, "Executing STA for power calculation");
1021 return power_inst.executePowerPerInst(m_sta, m_logger);
1022 }
1023
GetSupplyVoltage()1024 pair<double, double> IRSolver::GetSupplyVoltage()
1025 {
1026 SupplyVoltage supply_volt;
1027 return supply_volt.getSupplyVoltage(m_sta);
1028 }
1029
GetResult()1030 bool IRSolver::GetResult()
1031 {
1032 return m_result;
1033 }
1034
PrintSpice()1035 int IRSolver::PrintSpice()
1036 {
1037 DokMatrix* Gmat = m_Gmat->GetGMatDOK();
1038 map<GMatLoc, double>::iterator it;
1039
1040 ofstream pdnsim_spice_file;
1041 pdnsim_spice_file.open(m_spice_out_file);
1042 if (!pdnsim_spice_file.is_open()) {
1043 m_logger->error(
1044 utl::PSM,
1045 41,
1046 "Spice file {} did not open. Please check if it is a valid path.",
1047 m_spice_out_file);
1048 }
1049 vector<double> J = GetJ();
1050 int num_nodes = m_Gmat->GetNumNodes();
1051 int resistance_number = 0;
1052 int voltage_number = 0;
1053 int current_number = 0;
1054
1055 NodeLoc node_loc;
1056 for (it = Gmat->values.begin(); it != Gmat->values.end(); it++) {
1057 NodeIdx col = (it->first).first;
1058 NodeIdx row = (it->first).second;
1059 if (col <= row) {
1060 continue; // ignore lower half and diagonal as matrix is symmetric
1061 }
1062 double cond = it->second; // get cond value
1063 if (abs(cond) < 1e-15) { // ignore if an empty cell
1064 continue;
1065 }
1066
1067 string net_name = m_power_net;
1068 if (col < num_nodes) { // resistances
1069 double resistance = -1 / cond;
1070
1071 Node* node1 = m_Gmat->GetNode(col);
1072 Node* node2 = m_Gmat->GetNode(row);
1073 node_loc = node1->GetLoc();
1074 int x1 = node_loc.first;
1075 int y1 = node_loc.second;
1076 int l1 = node1->GetLayerNum();
1077 string node1_name = net_name + "_" + to_string(x1) + "_" + to_string(y1)
1078 + "_" + to_string(l1);
1079
1080 node_loc = node2->GetLoc();
1081 int x2 = node_loc.first;
1082 int y2 = node_loc.second;
1083 int l2 = node2->GetLayerNum();
1084 string node2_name = net_name + "_" + to_string(x2) + "_" + to_string(y2)
1085 + "_" + to_string(l2);
1086
1087 string resistance_name = "R" + to_string(resistance_number);
1088 resistance_number++;
1089
1090 pdnsim_spice_file << resistance_name << " " << node1_name << " "
1091 << node2_name << " " << to_string(resistance) << endl;
1092
1093 double current = node1->GetCurrent();
1094 string current_name = "I" + to_string(current_number);
1095 if (abs(current) > 1e-18) {
1096 pdnsim_spice_file << current_name << " " << node1_name << " " << 0
1097 << " " << current << endl;
1098 current_number++;
1099 }
1100
1101 } else { // voltage
1102 Node* node1 = m_Gmat->GetNode(row); // VDD location
1103 node_loc = node1->GetLoc();
1104 double voltage_value = J[col];
1105 int x1 = node_loc.first;
1106 int y1 = node_loc.second;
1107 int l1 = node1->GetLayerNum();
1108 string node1_name = net_name + "_" + to_string(x1) + "_" + to_string(y1)
1109 + "_" + to_string(l1);
1110 string voltage_name = "V" + to_string(voltage_number);
1111 voltage_number++;
1112 pdnsim_spice_file << voltage_name << " " << node1_name << " 0 "
1113 << to_string(voltage_value) << endl;
1114 }
1115 }
1116
1117 pdnsim_spice_file << ".OPTION NUMDGT=6" << endl;
1118 pdnsim_spice_file << ".OP" << endl;
1119 pdnsim_spice_file << ".END" << endl;
1120 pdnsim_spice_file << endl;
1121 pdnsim_spice_file.close();
1122 return 1;
1123 }
1124
Build()1125 bool IRSolver::Build()
1126 {
1127 bool res = true;
1128 ReadC4Data();
1129 if (res) {
1130 res = CreateGmat();
1131 }
1132 if (res) {
1133 res = CreateJ();
1134 }
1135 if (res) {
1136 res = AddC4Bump();
1137 }
1138 if (res) {
1139 res = m_Gmat->GenerateCSCMatrix();
1140 res = m_Gmat->GenerateACSCMatrix();
1141 }
1142 if (res) {
1143 m_connection = CheckConnectivity();
1144 res = m_connection;
1145 }
1146 m_result = res;
1147 return m_result;
1148 }
1149
BuildConnection()1150 bool IRSolver::BuildConnection()
1151 {
1152 bool res = true;
1153 ReadC4Data();
1154 if (res) {
1155 res = CreateGmat(true);
1156 }
1157 if (res) {
1158 res = AddC4Bump();
1159 }
1160 if (res) {
1161 res = m_Gmat->GenerateACSCMatrix();
1162 }
1163 if (res) {
1164 m_connection = CheckConnectivity();
1165 res = m_connection;
1166 }
1167 m_result = res;
1168 return m_result;
1169 }
1170 } // namespace psm
1171